一. 迭代器(iterator)
iterable,iterator,iteration
- 可迭代对象
可以通过for … in … 这类语句迭代的对象称为可迭代对象# 通过isinstance判断一个对象是否为iterable和iterator对象 from collections import Iterable, Iterator isinstance([], Iterable) # True: [] {} 'abc',False: 没有__iter__方法的类对象,数字 isinstance(iter([]), Iterator) # 有无__next__方法
- 迭代器本质
可迭代对象就是有个变量会记录访问到了第几条数据,以便下一次返回下一条数据,将这个变量称为迭代器。通过__iter__
方法提供迭代器。迭代一个可迭代对象时候,就是先获取该对象的迭代器,实现了该方法的即为可迭代对象。
for … in …循环本质也是先通过iter()函数获取可迭代对象Iterable的迭代器,然后不断调用next()函数,当遇到StopIteration异常后循环结束
注意:list()和tuple()等也能接收可迭代对象,自动生成迭代后的结果。 - 常用函数iter() 和 next()
通过iter()函数可以获取可迭代对象的迭代器,然后可以通过next()函数不断获取下一条数据,只直最后会抛出StopIteration异常。实质是调用__iter__
和__next__
方法。且实现了这两个方法的就是迭代器。
二. 生成器(generator)
生成器是一类特殊的迭代器
- 创建生成器
方法1:将列表生成式的[]改为(),此时的生成器G即是一个特殊的迭代器
方法2:def中带有yield关键字L = [x*2 for x in range(5)] # [0,2,4,6,8] G = (x*2 for x in range(5)) # <generator object <genexpr> at 0x000002AA841AA3B8>
将return 换成 yield,此时函数即是一个生成器
注意:-
用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中
-
yield关键字作用
- 保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起
- 将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用
-
Python3中的生成器可以使用return返回最终运行的返回值,而Python2中的生成器不允许使用return返回一个返回值(即可以使用return从生成器中退出,但return后不能有任何表达式)
-
可以使用next()函数让生成器从断点处继续执行,即唤醒生成器(函数)
-
send(),同next()。唯一好处可以向断点处加入一个附加数据
-
三. 协程(Coroutine)
- yield
- greenlet
from greenlet import greenlet
import time
def test1():
while True:
print('--A--')
gr2.switch()
time.sleep(0.5)
def test():
while True:
print('--B--')
gr1.switch()
time.sleep(0.5)
gr1 = greenlet(test1)
gr2 = greenlet(test2)
#切换到gr1中运行
gr1.switch()
- gevent
import gevent
def f(n):
for i in range(n):
print(gevent.getcurrent(), i)
# 用来模式一个耗时操作,注意不是time模块中的sleep
gevent.sleep(1)
g1 = gevent.spawn(f, 5)
g2 = gevent.spawn(f, 5)
g3 = gevent.spawn(f, 5)
g1.join()
g2.join()
g3.join()
六. 进程、线程、协程区别
- 进程是资源分配的单位
- 线程是操作系统调度的单位
- 进程切换需要的资源很最大,效率很低
- 线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
- 协程切换任务资源很小,效率高
- 多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中 所以是并发
七. 并发下载器
from gevent import monkey
import gevent
import urllib.request
# 有IO才做时需要这一句
monkey.patch_all()
def my_downLoad(file_name, url):
print('GET: %s' % url)
resp = urllib.request.urlopen(url)
data = resp.read()
with open(file_name, "wb") as f:
f.write(data)
print('%d bytes received from %s.' % (len(data), url))
gevent.joinall([
gevent.spawn(my_downLoad, "1.mp4", 'http://oo52bgdsl.bkt.clouddn.com/05day-08-%E3%80%90%E7%90%86%E8%A7%A3%E3%80%91%E5%87%BD%E6%95%B0%E4%BD%BF%E7%94%A8%E6%80%BB%E7%BB%93%EF%BC%88%E4%B8%80%EF%BC%89.mp4'),
gevent.spawn(my_downLoad, "2.mp4", 'http://oo52bgdsl.bkt.clouddn.com/05day-03-%E3%80%90%E6%8E%8C%E6%8F%A1%E3%80%91%E6%97%A0%E5%8F%82%E6%95%B0%E6%97%A0%E8%BF%94%E5%9B%9E%E5%80%BC%E5%87%BD%E6%95%B0%E7%9A%84%E5%AE%9A%E4%B9%89%E3%80%81%E8%B0%83%E7%94%A8%28%E4%B8%8B%29.mp4'),
])