#使用生成器去实现协程
为什么是生成器?因为生成器 类似暂停的功能。yield
先看一个简单的生成器
def func():
yield 1
yield 2
yield 3
return "TT"
if __name__ == "__main__":
gen = func()
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
"""
1
2
3
报错
StopIteration: TT
"""
生成器生成值和接收值
"""
生成器
不光可以 产出值
还可以 接收值
"""
def func():
# 1.可以产出值
# 2.可以接收值,调用方传递进来的值
html = yield "http://www.baidu.com"
print(html)
yield 2
yield 3
return "TT"
if __name__ == "__main__":
#启动 生成器除了next,还有send
gen = func()
url = next(gen)
# 如果是send,会抛出一个异常
# 可以gen.send(None)
# 在send发送非None之前,必须先启动生成器
# 1。send(None) 2.next()
html = "boby"
a = gen.send(html)
print(a)
# send方法可以传递值到生成器内部
# 同时还可以重启生成器,执行到下一个yield位置,同next方法
yield from
# python3.3新加的特性 yield from
# 在学习之前,先学习一个chain
from itertools import chain
"""
chain 可以将多个可迭代对象 拼接成一个,完成for循环
"""
a = [1,2,3,4]
b = ["aa","bb","cc"]
c = {"key11":"111","key22":"222"}
for i in chain(a,b,c,range(10,13)):
print(i)
# 接下来,我们自己实现一个chain
def my_chain(*args,**kwargs):
for my_interable in args:
for value in my_interable:
yield value
for i in my_chain(a,b,c,range(10,13)):
print(i)
#===================================================================
# yield from iterable 后面跟着一个可迭代对象
def my_chain(*args,**kwargs):
for my_interable in args:
yield from my_interable
"""
def my_chain(*args,**kwargs):
for my_interable in args:
yield from my_interable
for i in my_chain(a,b,c,range(10,13)):
print(i)
"""
#===================================================================
def g1(gen):
yield from gen
def main():
g = g1()
g.send(None)
"""
main 叫调用方
g1 是委托生成器
gen 是子生成器
yield from 会在调用方和子生成器之间 ,建立一个双向通道
子生成器yield出去的值,是直接交给main的
main发送的值,是直接发送给gen的,子生成器,而不是g=g1()
"""
yield from 例子
final_result = {}
def sales_sum(pro_name):
total = 0
nums = []
while True:
x = yield
print(pro_name+"销量: ", x)
if not x:
break
total += x
nums.append(x)
return total, nums
def middle(key):
while True:
final_result[key] = yield from sales_sum(key)
print(key+"销量统计完成!!.")
def main():
data_sets = {
"bobby牌面膜": [1200, 1500, 3000],
"bobby牌手机": [28,55,98,108 ],
"bobby牌大衣": [280,560,778,70],
}
for key, data_set in data_sets.items():
print("start key:", key)
m = middle(key)
m.send(None) # 预激middle协程
for value in data_set:
m.send(value) # 给协程传递每一组的值
m.send(None)
print("final_result:", final_result)
if __name__ == '__main__':
main()
"""
如果不使用上述方法,需要自己处理异常
"""
def sales_sum(pro_name):
total = 0
nums = []
while True:
x = yield
print(pro_name+"销量: ", x)
if not x:
break
total += x
nums.append(x)
return total, nums
if __name__ == "__main__":
my_gen = sales_sum("bobby牌手机")
my_gen.send(None)
my_gen.send(1200)
my_gen.send(1500)
my_gen.send(3000)
try:
my_gen.send(None)
except StopIteration as e:
result = e.value
print(result)
yield from 运行逻辑
"""
复制的
"""
#pep380
#1. RESULT = yield from EXPR可以简化成下面这样
# EXPR是表达式
#一些说明
"""
_i:子生成器,同时也是一个迭代器
_y:子生成器生产的值
_r:yield from 表达式最终的值
_s:调用方通过send()发送的值
_e:异常对象
"""
_i = iter(EXPR) # EXPR是一个可迭代对象,_i其实是子生成器;
try:
_y = next(_i) # 预激子生成器,把产出的第一个值存在_y中;
except StopIteration as _e:
_r = _e.value # 如果抛出了`StopIteration`异常,那么就将异常对象的`value`属性保存到_r,这是最简单的情况的返回值;
else:
while 1: # 尝试执行这个循环,委托生成器会阻塞;
_s = yield _y # 生产子生成器的值,等待调用方`send()`值,发送过来的值将保存在_s中;
try:
_y = _i.send(_s) # 转发_s,并且尝试向下执行;
except StopIteration as _e:
_r = _e.value # 如果子生成器抛出异常,那么就获取异常对象的`value`属性存到_r,退出循环,恢复委托生成器的运行;
break
RESULT = _r # _r就是整个yield from表达式返回的值。
"""
1. 子生成器可能只是一个迭代器,并不是一个作为协程的生成器,所以它不支持.throw()和.close()方法;
2. 如果子生成器支持.throw()和.close()方法,但是在子生成器内部,这两个方法都会抛出异常;
3. 调用方让子生成器自己抛出异常
4. 当调用方使用next()或者.send(None)时,都要在子生成器上调用next()函数,当调用方使用.send()发送非 None 值时,才调用子生成器的.send()方法;
"""
_i = iter(EXPR)
try:
_y = next(_i)
except StopIteration as _e:
_r = _e.value
else:
while 1:
try:
_s = yield _y
except GeneratorExit as _e:
try:
_m = _i.close
except AttributeError:
pass
else:
_m()
raise _e
except BaseException as _e:
_x = sys.exc_info()
try:
_m = _i.throw
except AttributeError:
raise _e
else:
try:
_y = _m(*_x)
except StopIteration as _e:
_r = _e.value
break
else:
try:
if _s is None:
_y = next(_i)
else:
_y = _i.send(_s)
except StopIteration as _e:
_r = _e.value
break
RESULT = _r
"""
看完代码,我们总结一下关键点:
1. 子生成器生产的值,都是直接传给调用方的;调用方通过.send()发送的值都是直接传递给子生成器的;如果发送的是 None,会调用子生成器的__next__()方法,如果不是 None,会调用子生成器的.send()方法;
2. 子生成器退出的时候,最后的return EXPR,会触发一个StopIteration(EXPR)异常;
3. yield from表达式的值,是子生成器终止时,传递给StopIteration异常的第一个参数;
4. 如果调用的时候出现StopIteration异常,委托生成器会恢复运行,同时其他的异常会向上 "冒泡";
5. 传入委托生成器的异常里,除了GeneratorExit之外,其他的所有异常全部传递给子生成器的.throw()方法;如果调用.throw()的时候出现了StopIteration异常,那么就恢复委托生成器的运行,其他的异常全部向上 "冒泡";
6. 如果在委托生成器上调用.close()或传入GeneratorExit异常,会调用子生成器的.close()方法,没有的话就不调用。如果在调用.close()的时候抛出了异常,那么就向上 "冒泡",否则的话委托生成器会抛出GeneratorExit异常。
"""
async 和 await
# python 3.5 以前,是使用生成器 完成协程任务的
# 3.5以后,为了语意更加明确,加入了async 和 await关键字,用于定义原生协程。
async def dwonloader(url):
return "1111"
async def download_url(url):
# from
html = await dwonloader(url) #Awaitable对象
return html
if __name__ == "__main__":
coro = download_url("https://www.baidu.com")
try:
coro.send(None)
except StopIteration as e:
result = e.value
print(result)