当前位置:
首页 > temp > python入门教程 >
-
【Python】笔记:协程
用作协程的生成器的基本行为
-
协程使用生成器函数定义: 定义体中有
yield
关键字
def simple_coroutine():
print('-> coroutine start')
x = yield # 因为 yield 右边没有值, 所以产出 None
print('-> coroutine received: ', x)
coro = simple_coroutine()
print(1, repr(coro))
next(coro) # 协程运行到第一个 yield 处暂停, 等待发送数据
coro.send(233) # 协程恢复, 运行至下一个 yield 或者终止
1 <generator object simple_coroutine at 0x000001C7B11C8930>
-> coroutine start
-> coroutine received: 233
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
Cell In [4], line 9
7 print(1, repr(coro))
8 next(coro)
----> 9 coro.send(233)
StopIteration:
协程有四种状态, 可以使用 inspect.getgeneratorstste(...)
来获取状态
因为 send
方法的参数会成为 暂停的 yield
表达式的值, 所以, 仅当协程处于暂停状态时才能调用 send
方法
因此, 一开始要用 next(coro)
激活协程 (这一步称为 预激 (prime) 协程)
用 coro.send(None)
效果一样, 但如果发送除 None
以外的值, 会抛出 TypeError
coro = simple_coroutine()
coro.send(233)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In [5], line 2
1 coro = simple_coroutine()
----> 2 coro.send(233)
TypeError: can't send non-None value to a just-started generator
from inspect import getgeneratorstate
def simple_coroutine2(a):
print('-> Started: a =', a)
b = yield a
print('-> Received: b =', b)
c = yield a + b
print('-> Received: c =', c)
coro2 = simple_coroutine2(14)
print(1, getgeneratorstate(coro2))
print(2, next(coro2)) # 预激, 执行到第一个 yield, 并产出 a
print()
print(3, getgeneratorstate(coro2))
print(4, coro2.send(66)) # 执行到第二个 yield, 并产出 a + b
print()
print(5, coro2.send(233)) # 执行结束, 抛出异常
1 GEN_CREATED
-> Started: a = 14
2 14
3 GEN_SUSPENDED
-> Received: b = 66
4 80
-> Received: c = 233
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
Cell In [9], line 18
16 print(4, coro2.send(66))
17 print()
---> 18 print(5, coro2.send(233))
StopIteration:
示例:使用协程计算移动平均值
def averager():
total = 0
count = 0
average = None
while True:
term = yield average
total += term
count += 1
average = total / count
aver = averager()
next(aver)
print(aver.send(233))
print(aver.send(234))
print(aver.send(235))
233.0
233.5
234.0
预激协程的装饰器
from functools import wraps
def coroutine(func):
@wraps(func)
def primer(*args, **kwargs):
gen = func(*args, **kwargs)
next(gen)
return gen
return primer
@coroutine
def averager():
total = 0
count = 0
average = None
while True:
term = yield average
total += term
count += 1
average = total / count
coro3 = averager()
print(coro3.send(233))
print(coro3.send(234))
print(coro3.send(235))
233.0
233.5
234.0
终止协程和异常处理
通过 throw()
和 close()
显式将异常抛给协程, 让其处理/退出
-
generator.throw(exc_type[, exc_value[, traceback]])
致使生成器在暂停处抛出指定异常, 如果生成器处理了异常, 那么代码会向前执行到下一个yield
表达式, 而产生的值会成为调用generator.thow
方法得到的返回值 -
generator.close()
致使生成器在暂停处抛出GeneratorExit
异常, 如果生成器没有处理这个异常, 或抛出了GeneratorExit
异常, 调用方不会报错; 如果收到GeneratorExit
异常, 生成器一定不能产出值, 否则解释器会抛出RuntimeError
class DemoException(Exception):
'''一个新的异常'''
def demo_exc_handing():
print('-> coroutine started')
while True:
try:
x = yield
except DemoException:
print('-> DemoException handled...')
else:
print('-> coroutine received: {!r}'.format(x))
raise RuntimeError("This line should never run")
exc_coro = demo_exc_handing()
next(exc_coro)
exc_coro.send(233)
exc_coro.send(666)
exc_coro.throw(DemoException)
exc_coro.send(999)
exc_coro.close()
-> coroutine started
-> coroutine received: 233
-> coroutine received: 666
-> DemoException handled...
-> coroutine received: 999
exc_coro = demo_exc_handing()
next(exc_coro)
exc_coro.send(233)
exc_coro.throw(ZeroDivisionError)
-> coroutine started
-> coroutine received: 233
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
Cell In [14], line 4
2 next(exc_coro)
3 exc_coro.send(233)
----> 4 exc_coro.throw(ZeroDivisionError)
Cell In [12], line 9, in demo_exc_handing()
7 while True:
8 try:
----> 9 x = yield
10 except DemoException:
11 print('-> DemoException handled...')
ZeroDivisionError:
# 扫尾工作
class DemoException(Exception):
'''一个新的异常'''
def demo_exc_handing():
print('-> coroutine started')
try:
while True:
try:
x = yield
except DemoException:
print('-> DemoException handled...')
else:
print('-> coroutine received: {!r}'.format(x))
finally:
print('-> coroutine ending')
让协程返回值
from collections import namedtuple
Result = namedtuple('Result', 'count average')
def averager():
total = 0
count = 0
average = None
while True:
term = yield
if term is None:
break # 便于退出
total += term
count += 1
average = total / count
return Result(count, average)
coro4 = averager()
next(coro4)
coro4.send(10)
coro4.send(30)
coro4.send(None) # 错误示范
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
Cell In [17], line 5
3 coro4.send(10)
4 coro4.send(30)
----> 5 coro4.send(None) # 错误示范
StopIteration: Result(count=2, average=20.0)
此例中, 发送 None
会导致循环终止, 协程结束, 抛出 StopIteration
Result()
会保存在 StopIteration.value
coro5 = averager()
next(coro5)
coro5.send(10)
coro5.send(20)
try:
coro5.send(None)
except StopIteration as exc:
result = exc.value
print(result)
Result(count=2, average=15.0)
使用 yield from
yield from x
会调用 iter(x)
, 取得迭代器
# 复习一下 yield from 的基本用法
def gen():
for c in 'AB':
yield c
for i in range(1, 3):
yield i
print(list(gen()))
['A', 'B', 1, 2]
def gen():
yield from 'AB'
yield from range(1, 3)
print(list(gen()))
['A', 'B', 1, 2]
# 例:计算平均数们
from collections import namedtuple
Result = namedtuple('Result', 'count average')
# 子生成器
def averager():
total = 0
count = 0
average = None
while True:
term = yield
if term is None:
break # 便于退出
total += term
count += 1
average = total / count
return Result(count, average)
# 委派生成器
def grouper(results, key):
while True:
results[key] = yield from averager()
# 调用方
def main(data):
results = {}
for key, values in data.items():
group = grouper(results, key)
next(group)
for value in values:
group.send(value)
group.send(None) # 终止当前的 averager
# print(results)
report(results)
# 输出报告
def report(results):
for key, result in sorted(results.items()):
group, unit = key.split(';')
print('{:2} {:5} averaging {:.2f}{}'.format(result.count, group, result.average, unit))
data = {
'human;kg': [65.78331, 71.51521, 69.39874, 68.2166, 67.78781, 68.69784, 69.80204, 70.01472, 67.90265, 66.78236, 66.48769, 67.62333, 68.30248, 67.11656, 68.27967, 71.0916, 66.461, 68.64927, 71.23033, 67.13118, 67.83379, 68.87881, 63.48115, 68.42187, 67.62804],
'human;cm': [112.9925, 136.4873, 153.0269, 142.3354, 144.2971, 123.3024, 141.4947, 136.4623, 112.3723, 120.6672, 127.4516, 114.143, 125.6107, 122.4618, 116.0866, 139.9975, 129.5023, 142.9733, 137.9025, 124.0449, 141.2807, 143.5392, 97.90191, 129.5027, 141.8501]
} # 注明一下, 数据来源于百度飞浆
main(data)
25 human averaging 130.31cm
25 human averaging 68.18kg
__EOF__
最新更新
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
Python初学者友好丨详解参数传递类型
如何有效管理爬虫流量?
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
SQL Server -- 解决存储过程传入参数作为s
关于JS定时器的整理
JS中使用Promise.all控制所有的异步请求都完
js中字符串的方法
import-local执行流程与node模块路径解析流程
检测数据类型的四种方法
js中数组的方法,32种方法
前端操作方法
数据类型
window.localStorage.setItem 和 localStorage.setIte
如何完美解决前端数字计算精度丢失与数