##03.协程
**说明:**
协程是利用其他任务在运行的时间做其他任务
###3.1迭代器
说明:迭代是访问集合元素的一种方式,迭代器是一个可以记住遍历的位置的对象,迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问结束,迭代器只能往前不会后退
**优点:占用内存空间小**
- 可迭代对象
可以对 list,tuple,str等类型的数据使用for…in…的循环语法从中一次拿到数据进行使用,我们把这样的过程称为遍历,也叫迭代
在开发的过程中,会出现一种场景,创建一个对象,对象里面有属性,列表里放了很多东西用for取里面的东西, 第一步:在创建的这个类里边必须实现__ iter__ (self)方法 第二步:__ iter__ (self)方法必须返回一个迭代器的对象,只要这个迭代器里面有__ iter__ (self),__ next__(self)方法,可以去取里面的值
代码演示:
import time
class Classmate(object):
def __init__(self):
self.names = list()
self.current_num = 0
def add(self, name):
self.names.append(name)
def __iter__(self):
"""如果想要一个对象称为一个可迭代的对象,即可以使用for,那么必须实现__iter__方法"""
return self
def __next__(self):
if self.current_num < len(self.names):
ret = self.names[self.current_num]
self.current_num += 1
return ret
else:
raise StopIteration # 检测到异常后传递到for循环,for循环终止
classmate = Classmate()
classmate.add("老王")
classmate.add("小郑")
classmate.add("老狗")
for name in classmate:
print(name)
time.sleep(1)
range和xrange的区别:xrange返回的是一个可以迭代的对象,什么时候调什么时候取,什么时候生成,占用极小内存的空间,range返回的是结果,占用大量的空间,迭代器存储的是生成的方式,而不是结果。
###3.2生成器 1.生成器 利用迭代器,我们可以在每次迭代获取数据(通过next0方法)时按照特定的规律进行生成·但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据·为了达到记录当前状态,并配合next(函数进行迭代使用,我们可以采用更简便的语法,即生成器(generator).生成器是一类特殊的迭代器。
2.创建生成器方法1: 要创建一一个生成器,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成()
In [151: L = [ x*2 for x in range(5)]
In [16]: L
0ut[16]: [0, 2, 4, 6, 8]
In [17]: G = ( x*2 for x in range(5))
In [18]: G
Out[18]: <generator object <genexpr> at @x7f626c132db0>
创建L和G的区别仅在于最外层的[]和(),L是一个列表,而G是一一个生成器·我们可以直接打印出列表L的每一个元素,而对于生成器G,我们可以按照迭代器的使用方法来使用,即可以通过next()函数、for循环、list()等方法使用。
3.创建生成器方法2 generator比较强大,如果推算的算法比较复杂,用类似的列表生成方式的for循环无法实现的时候,还可以用函数来实现
总结 ●使用了yield关键字的函数不再是函数,而是生成器,(使用了yield的函数就是生成器) ●yeld关键字有两点作用: 。保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起 。将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用 ●可以使用next()函数让生成器从断点处继续执行,即唤醒生成器(函数) ●Python3中的生 成器可以使用return返回最终运行的返回值,而Python2中的生成器不允许使用return返回一个返回值(仰可以使用retum从生成器中退出,但eturn后不能有任何表达式),
4.使用send唤醒 我们除了可以使用next值数来唤醒生成器继续执行外,还可以使用send0函数来唤醒执行.使用send0值数的一个好必是可以在唤醒的同时向断点处传入一个附加数据. ; 例子:执行到eld , gen最数作用暂时保存,返回的值temp接收下次c.send(‘pyton’) send发送过来的值 c.next()等价于 send(None)
总结: ●迭代器一大特点,保证了将来用极小的代码,极小的运行空间去生成想要的数据 ●生成器最大的特点yield,他可以让函数晚停执行,而且它没有将数据丢失,再执行时,它会恢复到上次晚停的地方开始执行 结论: ●迭代器能减少运行空间,实现循环,生成器能让一个函数晚停执行,什么时候想让它执行通过next()、send()让它恢复执行。 ●生成器、迭代器保存的都是生成数据的代码,而不是生成结果
###3.3生成器的用处——实现多任务
greenlet 为了更好的使用协程来完成多任务,python中使用greenlet模块对其封装 greenlet是一个类,调用greenlet(函数),他的返回值就是一个对象
gevent
greenlet已经实现了协程,但是这个还的人工切换,是不是觉得太麻烦了,不要捉急,python还有一个比greenlet更强大的并且能够自动切换任务的模块gevent
其原理是当一个greenlet遇到IO(指的是input output输入输出, 比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行
由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO
协程依赖于线程,线程依赖于进程,程序运行后为进程,进程是资源分配的单位,线程执行代码,一个线程只能做一件事情,想要多任务就用多线程,想要在单线程中实现多任务就创建协程
使用time模块中的sleep或是其他模块中的函数不想修改代码,又想继续实现功能,那么给程序打补丁导入模块:from gevent import monkey monkey.patch_all() 相当于替换操作——将需要延时的地方更换为gevent ●更简洁的方式:使用gevent.joinall()创建一个列表,gevent.joinall([gevent.spawn(xxx),gevent.spawn(xxx)······])更为简洁
进程、线程、协程对比 请仔细理解如下的通俗描述 ●有一个老板想要开个工厂进行生产某件商品(例如剪子) ●他需要花一些财力物力制作一条生产线,这个生产线上有很多的器件以及材料这些所有的为了能够生产剪子而准备的资源称之为:进程 ●只有生产线是不能够进行生产的,所以老板的找个工人来进行生产,这个工人能够利用这些材料最终一步步的将剪子做出来,这个来做事情的工人称之为:线程 ●这个老板为了提高生产率,想到3种办法: 1.在这条生产线上多招些工人,-起来做剪子,这样效率是成倍增长,即单进程多线程方式 2.老板发现这条生产线上的工人不是越多越好.因为一.条生产线的资源以及材料毕竟有限。所以老板又花了些财力物力购置了另外一条生产线,然后再招些工人这样效率又再一步提高了,即多进程多线程方式 3.老板发现,现在已经有了很多条生产线.并且每条生产线上已经有很多工人了(即程序是多进程的,每个进程中又有多个线程) . 为了再次提高效率,老板想了个损招.规定:如果某个员工在上班时临时没事或者再等待某些条件(比如等待另-个工人生产完谋道工序之后他才能再次工作) ,那么这个员工就利用这个时间去做其它的事情,那么也就是说:如果一个线程等待某些条件.可以充分利用这个时间去做其它事情·其实这就是:协程方式
简单总结
1.进程是资源分配的单位 2.线程是操作系统调度的单位 3.进程切换需要的资源很最大,效率很低 4.线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)5.协程切换任务资源很小,效率高 6.多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在-个线程中所以是并发

版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 一只小可乐! |