协程最大的意义就是:利用了原来在等待延时操作的时间去切换任务执行。
一,迭代器
迭代器最大的好处是:储存生成数据的方式,较储存生成数据的结果占用较少的内存。
若一个对象是可迭代对象,此对象不一定可以进行迭代;若一个对象是迭代器,则此对象一定可以进行迭代。迭代对象也可以同时为迭代器。
迭代一个对象的步骤为,判断对象是否为可迭代对象,若成立则调用iter(对象)方法并触发对象的__iter__方法的返回值(__iter__的返回值应该为一个迭代器),判断返回对象是否为一个迭代器,调用迭代器中的next(对象)方法并触发对象的__next__方法的返回值,若迭代器出现报错 StopIteration时,for循环将自动结束迭代,并不报错。
1,判断对象是否为可迭代对象:
在python中万物皆对象,只要在对象内部实现__iter__方法,则对象是可迭代对象,即可以使用for循环。
代码中判断:
from collections import Iterable
isinstance(对象名,Iterable) # 返回布尔值进行判断
2,调用iter方法得到对象的__iter__的返回的返回值
在认定对象对应类中有__iter__后,调用iter方法,对象自动调用__iter__中return,return的返回值应该为一个迭代器。
3,判断返回对象是否为一个迭代器
迭代器中应有__iter__方法和__next__方法。调用next(对象)方法并触发对象中的__next__方法中的返回值。
代码中判断:
from collections import Iterator
isinstance(对象名,Iterator) # 返回布尔值进行判断
4,判断迭代器如何结束
当迭代器 raise StopIteration时,for循环语句将自动处理,正常结束循环。
5,为了更好的理解,附上代码
二,生成器
生成器的一大好处是:储存生成数据的方式。较return可返回结果时暂定程序,而后继续执行。
生成器是一类特殊的迭代器,它不需要再像上面的类一样写__iter__()和__next__()
方法了, 使用更加方便,它依然可以使用next函数和for循环取值,而当调用next函数并触发__next__返回值无参数时,将会自动报错StopIteration(迭代器无此功能)。
1,创建生成器的方法
1)将列表推导式的[] 换成()
eg: nums = (x*2 for x in range(10))
next(nums) / for i in nums
2) 只要在def中有yield关键字的 就称为 生成器。在函数中只要有yield,那个这就不是一个函数,而是一个生成器模板,在调用函数时就是在创建一个生成器对象。
当代码执行到yield语句时,将暂停程序,并返回数据,依然可以使用next函数和for循环取值。
2,yield生成器在代码中如何执行
当代码执行到yield语句时,将暂停程序,并返回数据,依然可以使用next函数和for循环取值。
当调用next函数并触发__next__返回值无参数时,将自动报错StopIteration。多个生成器对象先后执行,相互无影响。
3,return 在 yield生成器中的作用
当yield生成器报错时,(Exception的错误原因.value)将返回return数据,如下所示:
4,send
因为send在应用于功能上于next相接近,在这里我们将send与next进行比较。
1)在传参上的区别:
next只能接受yield传过来的参数。
send能接受yield传过来的参数,同时将send的参数传给yield处。
注意:send不能接收第一次的数据,因为程序从第一行执行,send的参数无处可传。但可以使用生成器名.send(None).
2)在调用上的区别:
next(生成器名)
生成器名.send(None)
三,协程-yield
在yield生成器中,利用next()方法,实现协程的多任务。遇到延时将等待延时,不切换任务。
四,协程-greenlet
greenlet 是对于yield的一个简单的封装,将yield利用next控制线程执行替换成了自动执行(像穿针引线)。遇到延时将等待延时,不切换任务。
实现原理与yield近乎一样,只是更加方便了。
五,协程-gevent
gevent 是对于greenlet的内部封装,而遇到延时就切换到下一个任务继续执行。
gevent只识别自己的框架延时,为了让gevent框架识别其他延时操作则需要转换
gevent在线程延时的时候才会自动执行,可使用join阻塞使gevent执行程序。
为方便理解,代码如下:
import gevent
import time
from gevent import monkey
# 打补丁,让gevent框架识别耗时操作,比如:time.sleep,网络请求延时
monkey.patch_all()
# 任务1
def work1(num):
for i in range(num):
print("work1....")
time.sleep(0.2)
# gevent.sleep(0.2)
# 任务2
def work2(num):
for i in range(num):
print("work2....")
time.sleep(0.2)
# gevent.sleep(0.2)
def main():
# gevent.joinall([
# gevent.spawn(work1, 5),
# gevent.spawn(work2, 5)
# ])
# print('end')
#创建协程指定对应的任务
g1 = gevent.spawn(work1, 6)
g2 = gevent.spawn(work2, 6)
while True:
print("主线程中执行")
time.sleep(1)
if __name__ == '__main__':
main()
六,代码
import urllib.request
import gevent
from gevent import monkey
import re
url_list = list()
monkey.patch_all()
# def get_url():
# files = open('cat_url.txt','r').read()
# list1 = re.findall(r'http://img0.*?\.jpg',files,re.S)
# if list1:
# print(list1,len(list1))
# else:
# print('no match')
# return list1
lists = ('http://img06.tooopen.com/images/20180916/tooopen_sl_21540854823136.jpg','http://img06.tooopen.com/images/20180911/tooopen_sl_145217521750944.jpg',
'http://img08.tooopen.com/20181010/tooopen_sl_20520752729360.jpg')
def downloader(img_name, img_url):
req = urllib.request.urlopen(img_url)
img_content = req.read()
with open('./test_cat/'+img_name, "wb") as f:
f.write(img_content)
def main():
for index in range(len(lists)):
url_list.append(gevent.spawn(downloader,'cat'+str(index)+'.jpg',lists[index]))
gevent.joinall(url_list)
if __name__ == '__main__':
main()