字典为动词“to yield”给出了两个释义:产出和让步。对于 Python 生成器中的 yield 来说,这两个含义都成立。yield item 这行代码会产出一个值,提供给 next(...) 的调用方;此外,还会作出让步,暂停执行生成器,让调用方继续工作,直到需要使用另一个值时再调用 next()。调用方会从生成器中拉取值。

从句法上看,协程与生成器类似,都是定义体中包含 yield 关键字的函数。可是,在协程中,yield 通常出现在表达式的右边(例如,datum = yield),可以产出值,也可以不产出----yield 关键字后面没有表达式。协程可能会从调用方接收数据,调用方使用 .send(datum) 方法把数据提供给协程。

一:生成器如何进化成协程

自python中加入yield关键字后,又经过了一系列的演化:

yield 关键字可以在表达式中使用(a = yield b);

生成器 API 中增加了.send(value) 方法(生成器的调用方可以使用 .send(...) 方法发送数据,发送的数据会成为生成器函数中 yield 表达式的值);

PEP 342 添加了 .throw(...) 和 .close() 方法(前者的作用是让调用方抛出异常,在生成器中处理;后者的作用是终止生成器);

因此,生成器可以作为协程使用。协程是指一个过程,这个过程与调用方协作,产出由调用方提供的值。

协程最近的演进来自 Python 3.3实现的“PEP 380—Syntax for Delegating to a Subgenerator”(https://www.python.org/dev/peps/pep-0380/)。PEP 380 对生成器函数的句法做了两处改动:

生成器可以返回一个值;以前如果在生成器中给 return 语句提供值,会抛出 SyntaxError 异常;

新引入了 yield from 句法,使用它可以把复杂的生成器重构成小型的嵌套生成器,省去了之前把生成器的工作委托给子生成器所需的大量样板代码。

二:用作协程的生成器的基本行为

协程可以身处四个状态中的一个。当前状态可以使用inspect.getgeneratorstate(...) 函数确定,该函数会返回下述字符串中的一个。

GEN_CREATED:等待开始执行;

GEN_RUNNING:解释器正在执行(只有在多线程应用中才能看到这个状态);

GEN_SUSPENDED:在 yield 表达式处暂停;

GEN_CLOSED:执行结束;

一个简单的例子如下;

  1. >>> def simple_coro2(a):
  2. ... print('-> Started: a =', a)
  3. ... b = yield a
  4. ... print('-> Received: b =', b)
  5. ... c = yield a + b
  6. ... print('-> Received: c =', c)
  7. ...
  8. >>> my_coro2 = simple_coro2(14)
  9. >>> from inspect import getgeneratorstate
  10. >>> getgeneratorstate(my_coro2)
  11. 'GEN_CREATED'
  12. >>> next(my_coro2)
  13. -> Started: a = 14
  14. 14
  15. >>> getgeneratorstate(my_coro2)
  16. 'GEN_SUSPENDED'
  17. >>> my_coro2.send(28)
  18. -> Received: b = 28
  19. 42
  20. >>> my_coro2.send(99)
  21. -> Received: c = 99
  22. Traceback (most recent call last):
  23. File "<stdin>", line 1, in <module>
  24. StopIteration
  25. >>> getgeneratorstate(my_coro2)
  26. 'GEN_CLOSED' 

最先调用 next(my_coro2) 函数这一步通常称为“预激”(prime)协程(即,让协程向前执行到第一个 yield 表达式,准备好作为活跃的协程使用)。

关键的一点是,协程在 yield 关键字所在的位置暂停执行。在赋值语句中,=右边的代码在赋值之前执行。因此,对于 b = yield a 这行代码来说,等到客户端代码再激活协程时才会设定 b 的值。

simple_coro2 协程的执行过程分为 3 个阶段,如下图所示:

三:使用协程计算移动平均值

下面是一个计算移动平均值的协程:

  1. def averager():
  2. total = 0.0
  3. count = 0
  4. average = None
  5. while True:
  6. term = yield average
  7. total += term
  8. count += 1
  9. average = total/count
  10.  
  11. >>> coro_avg = averager()
  12. >>> next(coro_avg) #调用 next 函数,预激协程
  13. >>> coro_avg.send(10)
  14. 10.0
  15. >>> coro_avg.send(30)
  16. 20.0
  17. >>> coro_avg.send(5)
  18. 15.0

这个无限循环表明,只要调用方不断把值发给这个协程,它就会一直接收值,然后生成结果。仅当调用方在协程上调用 .close() 方法,或者没有对协程的引用而被垃圾回收程序回收时,这个协程才会终止。

调用 next(coro_avg) 函数后,协程会向前执行到yield 表达式,产出 average 变量的初始值——None,因此不会出现在控制台中。此时,协程在 yield 表达式处暂停,等到调用方发送值。coro_avg.send(10) 那一行发送一个值,激活协程,把发送的值赋给 term,并更新 total、count 和 average 三个变量的值,然后开始 while 循环的下一次迭代,产出 average 变量的值,等待下一次为term 变量赋值。

四:预激协程的装饰器

如果不预激,那么协程没什么用。调用 my_coro.send(x) 之前,记住一定要调用next(my_coro)。为了简化协程的用法,有时会使用一个预激装饰器。

下面就是一个预激装饰器的例子(Python3):

  1. from functools import wraps
  2.  
  3. def coroutine(func):
  4. @wraps(func)
  5. def primer(*args,**kwargs):
  6. gen = func(*args,**kwargs)
  7. next(gen)
  8. return gen
  9. return primer
  10.  
  11. @coroutine
  12. def averager2():
  13. total = 0.0
  14. count = 0
  15. average = None
  16. while True:
  17. term = yield average
  18. total += term
  19. count += 1
  20. average = total/count
  21.  
  22. >>> coro_avg = averager()
  23. >>> from inspect import getgeneratorstate
  24. >>> getgeneratorstate(coro_avg)
  25. 'GEN_SUSPENDED'
  26. >>> coro_avg.send(10)
  27. 10.0
  28. >>> coro_avg.send(30)
  29. 20.0
  30. >>> coro_avg.send(5)
  31. 15.0

注意,使用 yield from 句法调用协程时,会自动预激。

五:终止协程和异常处理

协程中未处理的异常会向上冒泡,传给 next 函数或 send 方法的调用方(即触发协程的对象)。

  1. >>> from coroaverager1 import averager
  2. >>> coro_avg = averager()
  3. >>> coro_avg.send(40)
  4. 40.0
  5. >>> coro_avg.send(50)
  6. 45.0
  7. >>> coro_avg.send('spam')
  8. Traceback (most recent call last):
  9. ...
  10. TypeError: unsupported operand type(s) for +=: 'float' and 'str'
  11. >>> coro_avg.send(60)
  12. Traceback (most recent call last):
  13. File "<stdin>", line 1, in <module>
  14. StopIteration

由于在协程内没有处理异常,协程会终止。如果试图重新激活协程,会抛出StopIteration 异常。

从 Python 2.5 开始,客户代码可以在生成器对象上调用两个方法:throw 和 close,显式地把异常发给协程。

1:generator.throw(exc_type[, exc_value[, traceback]])

使生成器在暂停的 yield 表达式处抛出指定的异常。如果生成器处理了抛出的异常,代码会向前执行到下一个 yield 表达式,而产出的值会成为调用 generator.throw方法得到的返回值。如果生成器没有处理抛出的异常,异常会向上冒泡,传到调用方的上下文中。

2:generator.close()

使生成器在暂停的 yield 表达式处抛出 GeneratorExit 异常。如果生成器没有处理这个异常,或者抛出了 StopIteration 异常(通常是指运行到结尾),调用方不会报错。如果收到 GeneratorExit 异常,生成器一定不能产出值,否则解释器会抛出RuntimeError 异常。生成器抛出的其他异常会向上冒泡,传给调用方。

示例如下:

  1. class DemoException(Exception):
  2. """为这次演示定义的异常类型。"""
  3.  
  4. def demo_exc_handling():
  5. print('-> coroutine started')
  6. while True:
  7. try:
  8. x = yield
  9. except DemoException:
  10. print('*** DemoException handled. Continuing...')
  11. else:
  12. print('-> coroutine received: {!r}'.format(x))
  13. raise RuntimeError('This line should never run.')
  14.  
  15. >>> exc_coro = demo_exc_handling()
  16. >>> next(exc_coro)
  17. -> coroutine started
  18. >>> exc_coro.send(11)
  19. -> coroutine received: 11
  20. >>> exc_coro.send(22)
  21. -> coroutine received: 22
  22.  
  23. >>> exc_coro.throw(DemoException)
  24. *** DemoException handled. Continuing...
  25. >>> getgeneratorstate(exc_coro)
  26. 'GEN_SUSPENDED'
  27.  
  28. >>> exc_coro.close()
  29. >>> from inspect import getgeneratorstate
  30. >>> getgeneratorstate(exc_coro)
  31. 'GEN_CLOSED'

六:让协程返回值

在Python2中,生成器函数中的return不允许返回附带返回值。在Python3中取消了这一限制,因而允许协程可以返回值:

  1. from collections import namedtuple
  2. Result = namedtuple('Result', 'count average')
  3.  
  4. def averager():
  5. total = 0.0
  6. count = 0
  7. average = None
  8. while True:
  9. term = yield
  10. if term is None:
  11. break
  12. total += term
  13. count += 1
  14. average = total/count
  15. return Result(count, average)
  16.  
  17. >>> coro_avg = averager()
  18. >>> next(coro_avg)
  19. >>> coro_avg.send(10)
  20. >>> coro_avg.send(30)
  21. >>> coro_avg.send(6.5)
  22. >>> coro_avg.send(None)
  23. Traceback (most recent call last):
  24. ...
  25. StopIteration: Result(count=3, average=15.5)

发送 None 会终止循环,导致协程结束,返回结果。一如既往,生成器对象会抛出StopIteration 异常。异常对象的 value 属性保存着返回的值。

注意,return 表达式的值会偷偷传给调用方,赋值给 StopIteration 异常的一个属性。这样做有点不合常理,但是能保留生成器对象的常规行为——耗尽时抛出StopIteration 异常。如果需要接收返回值,可以这样:

  1. >>> try:
  2. ... coro_avg.send(None)
  3. ... except StopIteration as exc:
  4. ... result = exc.value
  5. ...
  6. >>> result
  7. Result(count=3, average=15.5)

获取协程的返回值要绕个圈子,可以使用Python3.3引入的yield from获取返回值。yield from 结构会在内部自动捕获 StopIteration 异常。这种处理方式与 for 循环处理 StopIteration 异常的方式一样。对 yield from 结构来说,解释器不仅会捕获 StopIteration 异常,还会把value 属性的值变成 yield from 表达式的值。

七:使用yield from

yield from 是 Python3.3 后新加的语言结构。在其他语言中,类似的结构使用 await 关键字,这个名称好多了,因为它传达了至关重要的一点:在生成器 gen 中使用 yield from subgen() 时,subgen 会获得控制权,把产出的值传给 gen 的调用方,即调用方可以直接控制 subgen。与此同时,gen 会阻塞,等待 subgen 终止。

yield from 可用于简化 for 循环中的 yield 表达式。例如:

  1. >>> def gen():
  2. ... for c in 'AB':
  3. ... yield c
  4. ... for i in range(1, 3):
  5. ... yield i
  6. ...
  7. >>> list(gen())
  8. ['A', 'B', 1, 2]

可以改为

  1. >>> def gen():
  2. ... yield from 'AB'
  3. ... yield from range(1, 3)
  4. ...
  5. >>> list(gen())
  6. ['A', 'B', 1, 2]

yield from x 表达式对 x 对象所做的第一件事是,调用 iter(x),从中获取迭代器。因此,x 可以是任何可迭代的对象。

如果 yield from 结构唯一的作用是替代产出值的嵌套 for 循环,这个结构很有可能不会添加到 Python 语言中。

yield from 的主要功能是打开双向通道,把最外层的调用方与最内层的子生成器连接起来,这样二者可以直接发送和产出值,还可以直接传入异常,而不用在位于中间的协程中添加大量处理异常的样板代码。有了这个结构,协程可以通过以前不可能的方式委托职责。

PEP 380 使用了一些yield from使用的专门术语:

委派生成器:包含 yield from <iterable> 表达式的生成器函数;

子生成器:从 yield from 表达式中 <iterable> 部分获取的生成器;

调用方:调用委派生成器的客户端代码;

下图是这三者之间的交互关系:

委派生成器在 yield from 表达式处暂停时,调用方可以直接把数据发给子生成器,子生成器再把产出的值发给调用方。子生成器返回之后,解释器会抛出StopIteration 异常,并把返回值附加到异常对象上,此时委派生成器会恢复。

下面是一个求平均身高和体重的示例代码:

  1. from collections import namedtuple
  2.  
  3. Result = namedtuple('Result', 'count average')
  4.  
  5. # 子生成器
  6. def averager():
  7. total = 0.0
  8. count = 0
  9. average = None
  10. while True:
  11. # main 函数发送数据到这里
  12. print("in averager, before yield")
  13. term = yield
  14. if term is None: # 终止条件
  15. break
  16. total += term
  17. count += 1
  18. average = total/count
  19.  
  20. print("in averager, return result")
  21. return Result(count, average) # 返回的Result 会成为grouper函数中yield from表达式的值
  22.  
  23. # 委派生成器
  24. def grouper(results, key):
  25. # 这个循环每次都会新建一个averager 实例,每个实例都是作为协程使用的生成器对象
  26. while True:
  27. print("in grouper, before yield from averager, key is ", key)
  28. results[key] = yield from averager()
  29. print("in grouper, after yield from, key is ", key)
  30.  
  31. # 调用方
  32. def main(data):
  33. results = {}
  34. for key, values in data.items():
  35. # group 是调用grouper函数得到的生成器对象
  36. group = grouper(results, key)
  37. print("\ncreate group: ", group)
  38. next(group) #预激 group 协程。
  39. print("pre active group ok")
  40. for value in values:
  41. # 把各个value传给grouper 传入的值最终到达averager函数中;
  42. # grouper并不知道传入的是什么,同时grouper实例在yield from处暂停
  43. print("send to %r value %f now"%(group, value))
  44. group.send(value)
  45. # 把None传入groupper,传入的值最终到达averager函数中,导致当前实例终止。然后继续创建下一个实例。
  46. # 如果没有group.send(None),那么averager子生成器永远不会终止,委派生成器也永远不会在此激活,也就不会为result[key]赋值
  47. print("send to %r none"%group)
  48. group.send(None)
  49. print("report result: ")
  50. report(results)
  51.  
  52. # 输出报告
  53. def report(results):
  54. for key, result in sorted(results.items()):
  55. group, unit = key.split(';')
  56. print('{:2} {:5} averaging {:.2f}{}'.format(result.count, group, result.average, unit))
  57.  
  58. data = {
  59. 'girls;kg':[40, 41, 42, 43, 44, 54],
  60. 'girls;m': [1.5, 1.6, 1.8, 1.5, 1.45, 1.6],
  61. 'boys;kg':[50, 51, 62, 53, 54, 54],
  62. 'boys;m': [1.6, 1.8, 1.8, 1.7, 1.55, 1.6],
  63. }
  64.  
  65. if __name__ == '__main__':
  66. main(data) 

grouper 发送的每个值都会经由 yield from 处理,通过管道传给 averager 实例。grouper 会在 yield from 表达式处暂停,等待 averager 实例处理客户端发来的值。averager 实例运行完毕后,返回的值绑定到 results[key] 上。while 循环会不断创建 averager 实例,处理更多的值。

外层 for 循环重新迭代时会新建一个 grouper 实例,然后绑定到 group 变量上。前一个 grouper 实例(以及它创建的尚未终止的 averager 子生成器实例)被垃圾回收程序回收。

代码结果如下:

  1. create group: <generator object grouper at 0x7f34ce8458e0>
  2. in grouper, before yield from averager, key is girls;kg
  3. in averager, before yield
  4. pre active group ok
  5. send to <generator object grouper at 0x7f34ce8458e0> value 40.000000 now
  6. in averager, before yield
  7. send to <generator object grouper at 0x7f34ce8458e0> value 41.000000 now
  8. in averager, before yield
  9. send to <generator object grouper at 0x7f34ce8458e0> value 42.000000 now
  10. in averager, before yield
  11. send to <generator object grouper at 0x7f34ce8458e0> value 43.000000 now
  12. in averager, before yield
  13. send to <generator object grouper at 0x7f34ce8458e0> value 44.000000 now
  14. in averager, before yield
  15. send to <generator object grouper at 0x7f34ce8458e0> value 54.000000 now
  16. in averager, before yield
  17. send to <generator object grouper at 0x7f34ce8458e0> none
  18. in averager, return result
  19. in grouper, after yield from, key is girls;kg
  20. in grouper, before yield from averager, key is girls;kg
  21. in averager, before yield
  22.  
  23. create group: <generator object grouper at 0x7f34ce845678>
  24. in grouper, before yield from averager, key is girls;m
  25. in averager, before yield
  26. pre active group ok
  27. send to <generator object grouper at 0x7f34ce845678> value 1.500000 now
  28. in averager, before yield
  29. send to <generator object grouper at 0x7f34ce845678> value 1.600000 now
  30. in averager, before yield
  31. send to <generator object grouper at 0x7f34ce845678> value 1.800000 now
  32. in averager, before yield
  33. send to <generator object grouper at 0x7f34ce845678> value 1.500000 now
  34. in averager, before yield
  35. send to <generator object grouper at 0x7f34ce845678> value 1.450000 now
  36. in averager, before yield
  37. send to <generator object grouper at 0x7f34ce845678> value 1.600000 now
  38. in averager, before yield
  39. send to <generator object grouper at 0x7f34ce845678> none
  40. in averager, return result
  41. in grouper, after yield from, key is girls;m
  42. in grouper, before yield from averager, key is girls;m
  43. in averager, before yield
  44.  
  45. create group: <generator object grouper at 0x7f34ce845620>
  46. in grouper, before yield from averager, key is boys;kg
  47. in averager, before yield
  48. pre active group ok
  49. send to <generator object grouper at 0x7f34ce845620> value 50.000000 now
  50. in averager, before yield
  51. send to <generator object grouper at 0x7f34ce845620> value 51.000000 now
  52. in averager, before yield
  53. send to <generator object grouper at 0x7f34ce845620> value 62.000000 now
  54. in averager, before yield
  55. send to <generator object grouper at 0x7f34ce845620> value 53.000000 now
  56. in averager, before yield
  57. send to <generator object grouper at 0x7f34ce845620> value 54.000000 now
  58. in averager, before yield
  59. send to <generator object grouper at 0x7f34ce845620> value 54.000000 now
  60. in averager, before yield
  61. send to <generator object grouper at 0x7f34ce845620> none
  62. in averager, return result
  63. in grouper, after yield from, key is boys;kg
  64. in grouper, before yield from averager, key is boys;kg
  65. in averager, before yield
  66.  
  67. create group: <generator object grouper at 0x7f34ce8458e0>
  68. in grouper, before yield from averager, key is boys;m
  69. in averager, before yield
  70. pre active group ok
  71. send to <generator object grouper at 0x7f34ce8458e0> value 1.600000 now
  72. in averager, before yield
  73. send to <generator object grouper at 0x7f34ce8458e0> value 1.800000 now
  74. in averager, before yield
  75. send to <generator object grouper at 0x7f34ce8458e0> value 1.800000 now
  76. in averager, before yield
  77. send to <generator object grouper at 0x7f34ce8458e0> value 1.700000 now
  78. in averager, before yield
  79. send to <generator object grouper at 0x7f34ce8458e0> value 1.550000 now
  80. in averager, before yield
  81. send to <generator object grouper at 0x7f34ce8458e0> value 1.600000 now
  82. in averager, before yield
  83. send to <generator object grouper at 0x7f34ce8458e0> none
  84. in averager, return result
  85. in grouper, after yield from, key is boys;m
  86. in grouper, before yield from averager, key is boys;m
  87. in averager, before yield
  88. report result:
  89. 6 boys averaging 54.00kg
  90. 6 boys averaging 1.68m
  91. 6 girls averaging 44.00kg
  92. 6 girls averaging 1.58m

这个试验想表明的关键一点是,如果子生成器不终止,委派生成器会在yield from 表达式处永远暂停。如果是这样,程序不会向前执行,因为 yield from(与 yield 一样)把控制权转交给客户代码(即,委派生成器的调用方)了。

八:yield from的意义

把迭代器当作生成器使用,相当于把子生成器的定义体内联在 yield from 表达式中。此外,子生成器可以执行 return 语句,返回一个值,而返回的值会成为 yield from 表达式的值。

PEP 380 在“Proposal”一节(https://www.python.org/dev/peps/pep-0380/#proposal)分六点说明了 yield from 的行为。这里几乎原封不动地引述,不过把有歧义的“迭代器”一词都换成了“子生成器”,还做了进一步说明。上面的示例阐明了下述四点:

子生成器产出的值都直接传给委派生成器的调用方(即客户端代码);

使用 send() 方法发给委派生成器的值都直接传给子生成器。如果发送的值是None,那么会调用子生成器的 __next__() 方法。如果发送的值不是 None,那么会调用子生成器的 send() 方法。如果子生成器抛出 StopIteration 异常,那么委派生成器恢复运行。任何其他异常都会向上冒泡,传给委派生成器;

生成器退出时,生成器(或子生成器)中的 return expr 表达式会触发StopIteration(expr) 异常抛出;

yield from 表达式的值是子生成器终止时传给 StopIteration 异常的第一个参数。

yield from 的具体语义很难理解,尤其是处理异常的那两点。在PEP 380 中阐述了 yield from 的语义。还使用伪代码(使用 Python 句法)演示了 yield from 的行为。

若想研究那段伪代码,最好将其简化,只涵盖 yield from 最基本且最常见的用法:yield from 出现在委派生成器中,客户端代码驱动着委派生成器,而委派生成器驱动着子生成器。为了简化涉及到的逻辑,假设客户端没有在委派生成器上调用throw(...) 或 close() 方法。而且假设子生成器不会抛出异常,而是一直运行到终止,让解释器抛出 StopIteration 异常。上面示例中的脚本就做了这些简化逻辑的假设。

下面的伪代码,等效于委派生成器中的 RESULT = yield from EXPR 语句(这里针对的是最简单的情况:不支持 .throw(...) 和 .close() 方法,而且只处理 StopIteration 异常):

  1. _i = iter(EXPR)
  2. try:
  3. _y = next(_i)
  4. except StopIteration as _e:
  5. _r = _e.value
  6. else:
  7. while 1:
  8. _s = yield _y
  9. try:
  10. _y = _i.send(_s)
  11. except StopIteration as _e:
  12. _r = _e.value
  13. break
  14. RESULT = _r

但是,现实情况要复杂一些,因为要处理客户对 throw(...) 和 close() 方法的调用,而这两个方法执行的操作必须传入子生成器。此外,子生成器可能只是纯粹的迭代器,不支持 throw(...) 和 close() 方法,因此 yield from 结构的逻辑必须处理这种情况。如果子生成器实现了这两个方法,而在子生成器内部,这两个方法都会触发异常抛出,这种情况也必须由 yield from 机制处理。调用方可能会无缘无故地让子生成器自己抛出异常,实现 yield from 结构时也必须处理这种情况。最后,为了优化,如果调用方调用 next(...) 函数或 .send(None) 方法,都要转交职责,在子生成器上调用next(...) 函数;仅当调用方发送的值不是 None 时,才使用子生成器的 .send(...) 方法。

下面的伪代码,是考虑了上述情况之后,语句:RESULT = yield from EXPR的等效代码:

  1. _i = iter(EXPR)
  2. try:
  3. _y = next(_i)
  4. except StopIteration as _e:
  5. _r = _e.value
  6. else:
  7. while 1:
  8. try:
  9. _s = yield _y
  10. except GeneratorExit as _e:
  11. try:
  12. _m = _i.close
  13. except AttributeError:
  14. pass
  15. else:
  16. _m()
  17. raise _e
  18. except BaseException as _e:
  19. _x = sys.exc_info()
  20. try:
  21. _m = _i.throw
  22. except AttributeError:
  23. raise _e
  24. else:
  25. try:
  26. _y = _m(*_x)
  27. except StopIteration as _e:
  28. _r = _e.value
  29. break
  30. else:
  31. try:
  32. if _s is None:
  33. _y = next(_i)
  34. else:
  35. _y = _i.send(_s)
  36. except StopIteration as _e:
  37. _r = _e.value
  38. break
  39. RESULT = _r

上面的伪代码中,会预激子生成器。这表明,用于自动预激的装饰器与 yield from 结构不兼容。

from   https://www.cnblogs.com/gqtcgq/p/8126124.html

python协程--yield和yield from的更多相关文章

  1. (转)python协程2:yield from 从入门到精通

    原文:http://blog.gusibi.com/post/python-coroutine-yield-from/ https://mp.weixin.qq.com/s?__biz=MzAwNjI ...

  2. Python协程:从yield/send到async/await

    这个文章理好了脉落. http://python.jobbole.com/86069/ 我练 习了一番,感受好了很多... Python由于众所周知的GIL的原因,导致其线程无法发挥多核的并行计算能力 ...

  3. Python异步IO之协程(一):从yield from到async的使用

    引言:协程(coroutine)是Python中一直较为难理解的知识,但其在多任务协作中体现的效率又极为的突出.众所周知,Python中执行多任务还可以通过多进程或一个进程中的多线程来执行,但两者之中 ...

  4. [转载] Python协程从零开始到放弃

    Python协程从零开始到放弃 Web安全 作者:美丽联合安全MLSRC   2017-10-09  3,973   Author: lightless@Meili-inc Date: 2017100 ...

  5. 终结python协程----从yield到actor模型的实现

    把应用程序的代码分为多个代码块,正常情况代码自上而下顺序执行.如果代码块A运行过程中,能够切换执行代码块B,又能够从代码块B再切换回去继续执行代码块A,这就实现了协程 我们知道线程的调度(线程上下文切 ...

  6. 从yield 到yield from再到python协程

    yield 关键字 def fib(): a, b = 0, 1 while 1: yield b a, b = b, a+b yield 是在:PEP 255 -- Simple Generator ...

  7. python中和生成器协程相关的yield from之最详最强解释,一看就懂(四)

    如果认真读过上文的朋友,应该已经明白了yield from实现的底层generator到caller的上传数据通道是什么了.本文重点讲yield from所实现的caller到coroutine的向下 ...

  8. python中和生成器协程相关的yield之最详最强解释,一看就懂(一)

    yield是python中一个非常重要的关键词,所有迭代器都是yield实现的,学习python,如果不把这个yield的意思和用法彻底搞清楚,学习python的生成器,协程和异步io的时候,就会彻底 ...

  9. Python协程笔记 - yield

    生成器(yield)作为协程 yield实际上是生成器,在python 2.5中,为生成器增加了.send(value)方法.这样调用者可以使用send方法对生成器发送数据,发送的数据在生成器中会赋值 ...

  10. 用yield实现python协程

    刚刚介绍了pythonyield关键字,趁热打铁,现在来了解一下yield实现协程. 引用官方的说法: 与线程相比,协程更轻量.一个python线程大概占用8M内存,而一个协程只占用1KB不到内存.协 ...

随机推荐

  1. JVM虚拟机 - 内存

    在JVM虚拟机中,内存部分大致可以分为以下几类: Heap:堆 NonHeap:非堆 CodeCache:缓存编辑后的机器码的内存区域 CompressedClassSpace:类压缩空间 MetaS ...

  2. weex 项目搭建

    第一步:安装依赖 npm install -g weex-toolkit weex -v //查看当前weex版本 weex update weex-devtool@latest //@后标注版本后, ...

  3. 一个容易被忽视的css选择器

    之前学的的迷糊了,也不知道什么会什么不会了,跑去面试了.别人列出一堆css选择器,本以为选择器没啥的,结果到那个多类选择器翻车了,.a.b选择同时含a,b类名的,很尴尬所以回来仔细整理了一下.目前根据 ...

  4. 报错:'byte' does not name a type

    这个错误是因为你在.cpp/.h中使用 byte 这个类型,把他修改成int就ok了

  5. grafana快速入门

    入门 本指南将帮助您开始并熟悉Grafana.它假定您有一台正在运行的Grafana服务器,并至少添加了一个数据源. 初学者指南 观看10分钟的初学者指南,以建立仪表板,以快速介绍设置仪表板和面板. ...

  6. OpenSSL s_server / s_client 应用实例

    netkiller openssl tls 目录[-] 12.6. s_server / s_client 12.6.1. SSL POP3 / SMTP / IMAP 12.6.2. server ...

  7. CSS第二节

    div做页面布局的建议 把整个网页从上到下分成若干块(一般分三块:头,中间,尾部),每一块都按下面的思路 先写第一层,可以设置背景色,或者高度和垂直居中(line-height保证内容不超出高度),不 ...

  8. JS调试debug

    1. debugger; 我以前也说过,你可以在JavaScript代码中加入一句debugger;来手工造成一个断点效果.需要带有条件的断点吗?你只需要用if语句包围它: if (something ...

  9. 关于多行文本 textarea 在ios 真机上padding相对安卓较大问题

    问题: 多行文本组件是带有默认的padding的,然而,小程序的teatarea 在ios和安卓上显示的padding不一样,普遍ios的padding会比安卓的要明显的大.这种情况下我的想法是做兼容 ...

  10. redis redis-cli 操作指令

    默认选择 db库是 0 redis-cli -p 6379   查看当前所在“db库”所有的缓存key redis 127.0.0.1:6379> keys *   选择 db库 redis 1 ...