必会题
- 什么是协程?python中如何实现协程?常用的协议模块有哪些?
- 协程又称为微线程、纤程,也称为用户级线程,在不开辟线程的基础上完成多任务,也就是在单线程的情况下完成多任务,多个任务按照一定顺序交替执行。
- 通俗的理解只要在def里面看到一个yield关键字,那么就是表示协程;
- python中可以使用greenlet和Gevent模块来实现协程
- 简述线程、进程和多协程之间的区别。
- 进程之间不共享全局变量
- 线程之间共享全局变量,但是要注意资源竞争问题,解决办法:互斥锁或者线程同步。
- 创建进程的资源开销要比创建线程的资源开销大。
- 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
- 线程不能够独立执行,必须依存在进程中
- 多进程开发比单进程多线程开发稳定性要强
- 协程切换任务资源很小,效率高
- 使用进程实现文件夹的整体拷贝
''' 使用多进程方式实现文件夹的整体拷贝 ''' # 导入 os 模块 import os # 导入 进程模块 import multiprocessing # 文件拷贝任务 def copy_work(src_dir, dst_dir, file_name): # 查看进程对象,通过进程ID来区别启动了多少个进程 pid = multiprocessing.current_process().pid print(pid) # 拼接源文件的路径 src_file_path = src_dir + "/" + file_name # 拼接目标文件的路径 dst_file_path = dst_dir + "/" + file_name with open(dst_file_path, "wb") as dst_file: # 打源文件读取文件中的数据 with open(src_file_path, "rb") as src_file: while True: # 读取数据 src_file_data = src_file.read(1024) # 如果读取文件不为空就写入到新文件中,如果为空说明拷贝完成,结束循环 if src_file_data: # 写入到目标文件里面 dst_file.write(src_file_data) else: break def main(): # 源目录 src_dir = "/Users/KG/Desktop/test" # 目标目录 dst_dir = "/Users/KG/Desktop/test_copy" # 判断文件夹是否存在 if os.path.exists(dst_dir): print('目标文件夹已存在,不能拷贝') else: # 创建目标文件夹 os.mkdir(dst_dir) # 获取源目录里面文件的列表 file_name_list = os.listdir(src_dir) # 遍历文件里面获取文件名 for file_name in file_name_list: # 创建进程 p = multiprocessing.Process(target=copy_work, args=(src_dir,dst_dir,file_name)) #启动进程 p.start() # 主进程等待子进程执行完成以后程序再退出 p.join() if __name__ == '__main__': main()
练习题
1.我们所说的程序里的迭代是什么?
我们已经知道可以对list、tuple、str等类型的数据使用for...in...的循环语法从其中依次拿到数据进行使用,我们把这样的过程称为遍历,也叫迭代
2.怎么判断一个对象是不是可迭代对象?
使用 isinstance() 判断一个对象是否是 Iterable 对象:
可以通过for...in...这类语句迭代读取一条数据供我们使用的对象称之为可迭代对象
3.如何判断一个对象是否可以迭代?
from collections import Iterable flag = isinstance(obj,Iterable) print(flag)
4.可迭代对象的本质是什么?
具备了__iter__
方法的对象,就是一个可迭代对象
5.iter()函数与next()函数的作用?
list、tuple等都是可迭代对象,我们可以通过iter()函数获取这些可迭代对象的迭代器。然后我们可以对获取到的迭代器不断使用next()函数来获取下一条数据。iter()函数实际上就是调用了可迭代对象的__iter__
方法。
6.如何判断一个对象是否是迭代器?
from collections import Iterator flag = isinstance(obj,Iterator) print(flag)
7.迭代器的本质是什么?
一个实现了iter方法和next方法的对象,就是迭代器。
8.for … in … 循环的本质是什么?
for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束。
9.如何把可迭代对象直接转换为list或者tuple?
直接将可迭代对象传入list()和tuple()函数,得到的返回值即为list对象和tuple对象
10.创建生成器的方法有哪些?return
- 将列表推导式的[]改为()
- 在函数中使用yield关键字
11.yield关键字的作用?与return的区别?
yeild关键字
- 将一个函数变为生成器
- 保存当前运行状态,然后暂停执行,即将生成器挂起
- 将yield关键字后面的表达式的值返回
return
- 将最终运行的结果返回
- 结束当前函数
12.send方法的作用及注意点?
send方法可以用来唤醒yield挂起的生成器,并且向断点出发送附加数据
注意点:首次执行生成器不可以发送消息,需使用send(None),等价于next()
13.什么是协程?
在一个线程中的某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行,注意不是通过调用函数的方式做到的,并且切换的次数以及什么时候再切换到原来的函数都由开发者自己确定
14.协程和线程的区别?
在实现多任务时, 线程切换从系统层面远不止保存和恢复 CPU上下文这么简单。 操作系统为了程序运行的高效性每个线程都有自己缓存Cache等等数据,操作系统还会帮你做这些数据的恢复操作。 所以线程的切换非常耗性能。但是协程的切换只是单纯的操作CPU的上下文,所以一秒钟切换个上百万次系统都抗的住。
企业笔试题
1.谈谈你对多进程,多线程,以及协程的理解,项目是否用?
这个问题被问的概率相当之大,其实多线程,多进程,在实际开发中用到的很少,除非是那些对项目性能要求特别高的,有的开发工作几年了,也确实没用过,你可以这么回答,给他扯扯什么是进程,线程(cpython 中是伪多线程)的概念就行,实在不行你就说你之前写过下载文件时,用过多线程技术,或者业余时间用过多线程写爬虫,提升效率。
进程:一个运行的程序(代码)就是一个进程,没有运行的代码叫程序,进程是系统资源分配的最小单位,进程拥有自己独立的内存空间,所以进程间数据不共享,开销大。
线程: 调度执行的最小单位,也叫执行路径,不能独立存在,依赖进程存在一个进程至少有一个线程,叫主线程,而多个线程共享内存(数据共享,共享全局变量),从而极大地提高了程序的运行效率。
协程:是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。 协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。