python面试100讲 第五章(综合题)

第五章:综合题
第一题:with语句的用法
面试题1:with语句有什么作用,请用代码解释
面试题2:如何将with语句用于一个自定义类

面试题1:
with适用于对资源进行访问的场合,确保不管使用过程是否发生异常都会执行必要的清理工作(释放资源)

面试题2:
必须在类里面使用__enter__()和__exit__()方法

class MyClass:
    def __enter__(self):
        print('__enter__() is call')
        return self
    def process1(self):
        print('process1')
    def process2(self):
        print('process2')
        x = 1/0
    def __exit__(self, exc_type, exc_val, traceback):
        print('__exit__() is call')
        print(f'type:{exc_type}')
        print(f'value:{exc_val}')
        print(f'trace:{traceback}')
with MyClass() as f:
    f.process1()
    f.process2()
输出为:
__enter__() is call
process1
process2
__exit__() is call
type:<class 'ZeroDivisionError'>
value:division by zero
trace:<traceback object at 0x00000221AB785648>
Traceback (most recent call last):
  File "C:/my/pycharm_work/ceshi.py", line 17, in <module>
    f.process2()
  File "C:/my/pycharm_work/ceshi.py", line 9, in process2
    x = 1/0
ZeroDivisionError: division by zero

Process finished with exit code 1

###############################################
第二题:1.读文件的内容
2.统计字符数
3.求出现次数最多的字符
面试题1:现在有一个文本文件,要求得到该文本文件中出现次数最多的字符,最后输出该字符和出现的次数,空白符除外。

with open('文件名.txt','r') as f:
	data = f.read()

#key:在文本文件中出现的字符
#value:int类型,表示key指定的字符出现的总次数
#maxChar:表示当前统计出现频率最高的字符

d = {}
maxChar = ''
for c in data:
	if c.isspace():
		continue
	if d.get(c) is None:
		d[c] = 1
		if maxChar = ''
			maxChar = c
	else:
		d[c] += 1
		if d[maxChar] < d[c]
			maxChar = c

print(maxChar)
print(d[maxchar])

###############################################################
第三题:1.isinstance函数的用法
面试题1:如何区分调用的是函数还是方法

class MyClass:
	def process(self):
		pass

def process():
	pass

#第一种
print(type(MyClass().process))		#输出为:<class'method'>


 print(type(process))				#输出为:<class'function'>

#第二种
print(type(MyClass().process).__name__ =='method')   # 输出为:True
		
print(type(process).__name__ == 'function')			 # 输出为:True

#第三种
from type import MethodType,FunctionType

print(isinstance(MyClass().process,FunctionType))	# 输出为:False

print(isinstance(process,FunctionType))				# 输出是:True

总结:
通过isinstance函数可以判断调用的是函数还是方法,如果是函数,类型是FunctionType;如果是方法,类型是MethodType

#################################################
第四题:1.@staticmethod的用法
2.@classmethod的用法
3.二者的区别

共同点:
都是用来声明静态方法的,(调用方法不需要实例化,直接 类名.方法名)

区别:
1.@staticmethod不需要表示自身对象的self和自身类的cls参数,就像普通函数一样定义
2.@classmethod也不需要self参数,但第一个参数要是表示自身的cls参数,避免硬编码。(就是避免下面例子中像在staticmethod里面那样引用静态变量的时候直接是类名.静态变量,如果类名改变这里的也要跟着改变)

class MyClass:
	bar = 1 #静态变量
	def __init__(self):
		self.count = 20 #成员变量

	def process(self):
		print('process',self.count)

	@staticmethod
	def static_pcocess():
		print('static_process')	
		print(MyClass.bar)		#可以引用静态变量,但是如果类名改变这里的也要改变,比较麻烦,所以看下面那个静态方法:

	@classmethod
	def class_process(cls):
		print('class_process')
		print(cls.bar)		#这里的cls直接就是类本身,不用担心类名改变的问题
		print(cls)
		cls().process()		#cls是类本身,所以可以实例化调用类里面的方法
		print(cls().count)

	

print(MyClass.bar)			#静态变量不需要实例化就可以调用;但是成员变量需要实例化才能调用
MyClass.static_process()
MyClass.class_process()
MyClass.bar = 20
print(MyClass.static_process())

输出为:
1
static_process
1
class_process
1
<class ‘main.MyClass’>
process 20
20
static_process
20
None
#################################################第五题:1.请用代码说明hasattr,getattr和setattr的作用

hasattr:可以判断一个对象是否包含某个属性
getattr:可以获取对象中某一个属性的值
setattr:可以设置对象中某一个属性的值

class Person():
	def __init__(self):
		self.name = 'sadd'
		self.age = 22
	def show(self):
		print(self.name)
		print(self.age)

if hasattr(Person,'show'):
	print('存在show方法')

person = Person()
setattr(person,'sex','男')
setattr(person,'age',88)
print(getattr(person,'sex'))
print(getattr(person,'age'))
print(getattr(person,'name'))

#################################################第六题:
1.请阐述什么是lambda表达式,并用代码描述lambda表达式的应用场景

lambda表达式:就是匿名函数,可以作为参数传给函数或方法

a = [('a',1),('b',2),('d',3)]
a_1 = list(map(lambda x:x[0],a))
a_2 = list(map(lambda x:x[1],a))
print(a_1)
print(a_2)

输出为:
[‘a’, ‘b’, ‘d’]
[1, 2, 3]

#################################################
第七题:python生成器的定义和使用
面试题1:编写一个生成器,将二维列表转换为一维列表。

Python生成器(迭代)
yield

def myGenerator():
	numLost = [1,2,3,4,5,6,7,8]
	for num in numList:
		yield num

for num in myGenerator():
	print(num,end = ' ')
print()       #输出为: 1 2 3 4 5 6 7 8

面试题1:

a = [[1,2,3],[4,3,2],[1,2,3,5,7]]
def fun(list1):
	for i in list1:
		for j in i:
			yield element

n = list(fun(list1))
print(n)          # 输出为:[1, 2, 3, 4, 3, 2, 1, 2, 3, 5, 7]

#######################################################第八题:递归生成器的编写和使用
面试题1:请编写一个生成器,将任意多维的列表转换为一维列表

a = [1,2,[3,5,3,6,[4,56,2,[4,3]]],[1,2,3]]

def fun1(a):
	try:
		for i in a:
			for j in fun1(i):
				yield j
	except TypeError:
		yield a          #迭代单个值

for n in fun1(a):
	print(n,end = ' ')

#######################################################
第九题:
获取时间日期。
通过localtime函数处理后的时间可以直接获取更详细的信息。

import time

a = time.localtime(time.time())
print(a)
print(type(a))

print(a.tm_year)
print(a.tm_mon)
print(a.tm_mday)
print(a.tm_hour)
print(a.tm_yday)

#######################################################第十题:掌握进程间通信的方法
面试题1:用python创建两个进程,在这两个进程之间如何通信呢?
可以使用队列在进程之间进行通信(共享数据)。

from multiprocessing import Queue,Process
import time,random

list1 = ["Java","Python","C"]

def write(queue):
	for value in list1:
		print(f"正在向队列中添加数据-->{value}")
		queue.put_nowait(value)
		time.sleep(random.random())

def read(queue):
	while True:
		if not queue.empty():
			value = queue.get_nowait()
			print(f"从队列中读取的数据为-->{value}")
			time.sleep(random.random())
		else:
			break

queue = Queue()
write_data = Process(target=write,args=(queue,))
read_data = Process(target=read,args=(queue,))

if __name__ == '__main__':
	write_data.start()
	write_data.join()

	read_data.start()
	read_data.join()

	print('OK')

#######################################################
第十一题:threading.local()的用法
面试题1:如何创建和使用在线程内部用的全局对象。
注意:此处的全局对象只针对那个线程,对那个线程来说是全局对象。

local类用于创建一个全局对象,不过该对象只能在线程内部使用,也就是说,全局是针对一个线程而言的。

import threading 
import time 

a = threading.local()    #创建针对一个线程来说的全局对象

def worker():
	a.x = 0            #添加一个全局变量
	for i in range(20):
		time.sleep(0.01)
		a.x += 1        #最终结果为20,下一句打印
	print(threading.current_thread(),a.x)

for i in range(10):
	threading.Thread(target=worker).start()   #创建10个线程,会发现每次线程的x都是20
						  #所以每个a.x都只是针对那个线程本身来说的全局对象。

######################################################
第十二题:
面试题1:请描述一下什么是协程,请举例说明
面试题2:协程中有哪两个运行任务的函数,如何使用

面试题1:
协程:又称微线程,纤程,英文名:Corouting
使用async修饰要运行的函数,在运行协程函数时,需要使用await

通过async/await语法进行声明,是编写异步应用的推荐方式

协程的基本用法:

import asyncio

async def main():
	print('hello')
	await asyncio.sleep(1)
	print('world')

asyncio.run(main())

输出为:
hello
world

面试题2:
run/create_tassk

(1)run

import asyncio
import time

async def say_after(delay,what):
	await asyncio.sleep(delay)
	print(what)

async def myfun():
	print(f'开始时间:{time.strftime("%X")}')
	await say_after(1,'hello')   #因为函数say_after使用关键字async修饰了,所以使用的时候必须用await,不用会报错
	await say_after(2,'world')

	print(f'执行完成:{time.strftime("%X")}')
asyncio.run(myfun())

(2)create_task

import asyncio
import time

async def say_after(delay,what):	#创建异步函数/协程函数
	await asyncio.sleep(delay)
	print(what)

async def myfun1():
	#创建两个任务
	task1 = asyncio.create_task(
			say_after(1,'hello')
			)
	task2 = asyncio.create_task(
			say_after(2,'world')
			)

	print(f'开始时间:{time.strftime("%X")}')
	#执行任务
	await task1
	await task2

	print(f'结束时间:{time.strftime("%X")}s')
asyncio.run(myfun1())

#######################################################
第十三题:
面试题1:请解释什么是线程锁,举例说明如何使用线程锁。

线程锁:目的是将一段代码锁住,一旦获得锁权限,除非释放线程锁,否则其他任何代码都无法获得锁权限。

为什么需要线程锁:
由于多线程同时在完成特定的操作时,由于并不是原子操作,所以在完成操作的过程中可能被打断,去做其他的操作,可能产生脏数据。

例如:一个线程读取变量n(初始值是0),然后n++,最后输出n
当访问n++后,被打断,由另外的线程做同样的工作,这时n被加了2次,所以n最后等于2,而不是1.

from atexit import register
from threading import Thread,Lock,currentThread
from time import sleep,ctime
import random

lock = Lock()

def fun():
	lock.acquire()   #加锁   将下面的操作变为原子操作,就是下面的操作在执行过程中不能被别的线程所打断。
	for i in range(3):
		print('Thread Name','=',currentThread().name,'i=',i)  #currentThread().name 获取当前线程名
		sleep(random.randint(1,5))		#延时1.5s
	lock.release()

def main():
	for i in range(3):
		Thread(target=fun).start()      #创建三个线程

@register				#当线程运行完毕时会自动调用这个装饰函数
def exit():
	print('线程执行完毕:',ctime())

main()

输出为:
Thread Name = Thread-1 i= 0
Thread Name = Thread-1 i= 1
Thread Name = Thread-1 i= 2
Thread Name = Thread-2 i= 0
Thread Name = Thread-2 i= 1
Thread Name = Thread-2 i= 2
Thread Name = Thread-3 i= 0
Thread Name = Thread-3 i= 1
Thread Name = Thread-3 i= 2
线程执行完毕: Mon Mar 9 14:42:22 2020

小节:
使用Lock函数创建线程锁,使用lock.acquire方法加锁,使用lock.release方法解锁。在加锁后,任何代码执行acquire方法,都会被阻塞,直到将该锁释放。

#######################################################
第十四题:
面试题1:描述一下什么是信号量,如何使用信号量,请举例说明。

信号量:最古老的同步原语之一,是一个计数器,用于记录资源的消耗情况,当资源消耗时递减,当资源释放时递增,可以认为信号量代表资源是否可用。

from threading import BoundedSemaphore
Max = 3    #规定信号量最大拥有的资源为3。意思就是最多可以消耗3次资源

#创建对象
semaphore = BoundedSemaphore(Max)

print(semaphore._value)     #查看还可以消耗的资源量

semaphore.acquire()          #调用一次消耗一次资源
semaphore.acquire()
semaphore.acquire()
#上面调用3次,所以3个资源已经被消耗完
print(semaphore._value)

#semaphore.acquire()         #因为资源已经被消耗完了(_value的值是0),所以再次调用acquire方法会被阻塞,下面就不会运行了.
#print(semaphore._value)
#在acquire()方法中有个默认参数,默认值为True,意思就是默认为加锁了,所以上面如果消耗完了会被阻塞。
#如果给默认参数设置值为False,那么在未消耗完之前时print(semaphore.acquire(False))会输出True,
#如果在消耗完之后则返回False。

#下面是释放资源:::
semaphore.release()     #第一次释放资源
print(semaphore._value)

semaphore.release()     #第二次释放资源
semaphore.release()     #第三次释放资源
print(semaphore._value)

#semaphore.release()     #第四次释放资源   因为没有资源被占用,所以释放的话会报错!

输出为:
3
0
1
3

发布了65 篇原创文章 · 获赞 50 · 访问量 3589

猜你喜欢

转载自blog.csdn.net/qq_44907926/article/details/104754471