我读到了yield
扩展语法,所以如果我有:
def numgen(N):
for i in range(N):
n = yield i
if n:
yield n
我可以考虑它:
def numgen(N):
n = yield from range(N)
if n:
yield n
但是我注意到,如果我这样做,在我编写第二个生成器之后:
g = numgen(10)
next(g)
g.send(54)
我收到以下错误:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in gensquare
AttributeError: 'range_iterator' object has no attribute 'send'
那是怎么回事?如何将值发送到numgen
生成器对象?
答案 0 :(得分:5)
range()
不是生成器,它没有generator.send()
method。
yield
expression documentation中明确记录了这一点:
使用
yield from <expr>
时,它会将提供的表达式视为子实例。该子转换器生成的所有值都直接传递给当前生成器方法的调用者。 使用send()
传入的任何值以及使用throw()
传递的任何异常都会传递给基础迭代器(如果它具有相应的方法)。如果不是这种情况,则send()
会引发AttributeError
或TypeError
,而throw()
会立即引发传递的异常。
强调我的。
您正在尝试向range()
迭代器发送值,但它没有.send()
方法。
range()
只是序列,而不是生成器对象;你可以为它创建多个迭代器,你可以测试一个数字是否是序列的成员,询问它的长度等等。
请注意,您的“重构”根本不是一回事;在您的原始n
中分配了您通过generator.send()
发送的任何内容;在您的第二个版本yield from
中返回子迭代器结束时引发的value
异常的StopIteration
属性。如果子迭代器本身是一个生成器,您可以通过手动引发StopIteration(value)
或使用return
语句来设置该值。 yield from
无法返回使用generator.send()
发送的值,因为这些值会传递给子生成器。
再次,从文档:
当底层迭代器完成时,凸起的
StopIteration
实例的value属性将成为yield表达式的值。它可以在提升StopIteration
时显式设置,也可以在子迭代器是生成器时自动设置(通过从子生成器返回值)。
因此,您的第一个版本设置为接收N
条消息,同时产生i
for
目标,任何已发送的值为true-thy,而另一个传递任何已发送的消息对于一个degelated-to生成器,然后只有StopIteration
值,如果它是true-thy 一次,在委托迭代器完成之后。