第五章:综合题
第一题: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