Python : 生成器中 send() 的行为
Python : Behaviour of send() in generators
我在 python 3 中尝试使用生成器并编写了这个相当人为的生成器:
def send_gen():
print(" send_gen(): will yield 1")
x = yield 1
print(" send_gen(): sent in '{}'".format(x))
# yield # causes StopIteration when left out
gen = send_gen()
print("yielded {}".format(gen.__next__()))
print("running gen.send()")
gen.send("a string")
输出:
send_gen(): will yield 1
yielded 1
running gen.send()
send_gen(): sent in 'a string'
Traceback (most recent call last):
File "gen_test.py", line 12, in <module>
gen.send("a string")
StopIteration
所以 gen.__next__()
到达行 x = yield 1
并产生 1。我认为 x
会被分配给 None
,然后 gen.send()
会寻找next yield
语句因为x = yield 1
是"used",然后得到一个StopIteration
.
相反,似乎 发生的是 x
被发送 "a string",它被打印出来,然后 然后 python 尝试寻找下一个 yield
并得到 StopIteration
.
所以我试试这个:
def send_gen():
x = yield 1
print(" send_gen(): sent in '{}'".format(x))
gen = send_gen()
print("yielded : {}".format(gen.send(None)))
输出:
yielded : 1
但是现在没有错误了。在将 x
分配给 None
之后,send()
似乎没有尝试寻找 next yield
语句。
为什么行为略有不同?这与我启动发电机的方式有关吗?
这里的关键区别在于,您在第一个示例中点击了生成器两次,但在第二个示例中您只点击了生成器一次。
当你定义一个 coroutine,即你打算将参数发送到其中的生成器时,你必须通过前进到第一个 yield 来预先 'prime' 它陈述。只有这样你才能发送值。在第一个示例中,您通过在尝试 send
.
之前调用 gen.__next__()
明确地完成了此操作
在第二个示例中,您还通过执行 gen.send(None)
启动它(请注意,发送 None
实际上等同于调用 gen.__next__()
或 next(gen)
)。但是你没有尝试第二次发送值,所以在那种情况下没有 StopIteration
。生成器只是停在那里,在 yield 语句处暂停,等待您再次点击它,这也是您之后还没有看到打印的原因。
另一点需要注意的是,如果您在第二个示例中发送了 None
以外的任何内容,则会出现错误:
TypeError: can't send non-None value to a just-started generator
这就是我在 'priming' 协程中所说的。
行为没有不同;在第二个设置中,您从未超越生成器中的第一个 yield
表达式。请注意 StopIteration
是 而不是错误 ;这是正常行为,只要生成器结束就会触发预期的信号。在您的第二个示例中,您从未到达生成器的末尾。
每当生成器到达 yield
表达式时,执行会暂停 就在那里 ,表达式在恢复之前无法在生成器中生成任何内容。 gen.__next__()
或 gen.send()
都将从该点恢复执行,yield
表达式生成 gen.send()
或 None
传入的值。如果有帮助,您可以将 gen.__next__()
视为 gen.send(None)
。这里要意识到的一件事是 gen.send()
有 yield
return 发送值 first,then 生成器继续到下一个 yield
.
因此,给定您的第一个示例生成器,会发生这种情况:
gen = send_gen()
创建生成器对象。代码暂停在函数的最顶部,什么都不执行。
你要么叫gen.__next__()
要么叫gen.send(None)
;生成器开始并执行直到第一个 yield
表达式:
print(" send_gen(): will yield 1")
yield 1
现在执行暂停。 gen.__next__()
或 gen.send(None)
现在调用 return 1
,yield 1
产生的值。因为生成器现在已暂停,所以 x = ...
赋值还不能发生!只有当发电机再次恢复时才会发生这种情况。
您在第一个示例中调用 gen.send("a string")
,不要在第二个示例中调用 any。所以对于第一个例子,现在恢复生成器函数:
x = <return value of the yield expression> # 'a string' in this case
print(" send_gen(): sent in '{}'".format(x))
现在函数 结束 ,所以 StopIteration
被引发。
因为您从未在第二个示例中恢复生成器,所以未到达生成器的末尾并且没有引发 StopIteration
异常。
请注意,因为生成器从函数的顶部开始,所以在那个点上没有 yield
表达式到 return 无论你用 gen.send()
发送什么,所以第一个 gen.send()
值必须始终为 None
否则会引发异常。最好对生成器 'prime' 使用显式 gen.__next__()
(或者更确切地说 next(gen)
函数调用),这样它就会在第一个 yield
表达式处暂停。
从技术上讲,这是一个协程而不是生成器:
- 当我们调用send_gen()时,协程对象被创建
- 当调用 gen.___next____() 时 [我们应该使用 next(gen) ],生成器函数被调用 returns 1 并阻塞
- 当gen.send("a string")被调用时,协程被唤醒并处理输入(这里打印"a string")
- 然后协程退出
我在 python 3 中尝试使用生成器并编写了这个相当人为的生成器:
def send_gen():
print(" send_gen(): will yield 1")
x = yield 1
print(" send_gen(): sent in '{}'".format(x))
# yield # causes StopIteration when left out
gen = send_gen()
print("yielded {}".format(gen.__next__()))
print("running gen.send()")
gen.send("a string")
输出:
send_gen(): will yield 1
yielded 1
running gen.send()
send_gen(): sent in 'a string'
Traceback (most recent call last):
File "gen_test.py", line 12, in <module>
gen.send("a string")
StopIteration
所以 gen.__next__()
到达行 x = yield 1
并产生 1。我认为 x
会被分配给 None
,然后 gen.send()
会寻找next yield
语句因为x = yield 1
是"used",然后得到一个StopIteration
.
相反,似乎 发生的是 x
被发送 "a string",它被打印出来,然后 然后 python 尝试寻找下一个 yield
并得到 StopIteration
.
所以我试试这个:
def send_gen():
x = yield 1
print(" send_gen(): sent in '{}'".format(x))
gen = send_gen()
print("yielded : {}".format(gen.send(None)))
输出:
yielded : 1
但是现在没有错误了。在将 x
分配给 None
之后,send()
似乎没有尝试寻找 next yield
语句。
为什么行为略有不同?这与我启动发电机的方式有关吗?
这里的关键区别在于,您在第一个示例中点击了生成器两次,但在第二个示例中您只点击了生成器一次。
当你定义一个 coroutine,即你打算将参数发送到其中的生成器时,你必须通过前进到第一个 yield 来预先 'prime' 它陈述。只有这样你才能发送值。在第一个示例中,您通过在尝试 send
.
gen.__next__()
明确地完成了此操作
在第二个示例中,您还通过执行 gen.send(None)
启动它(请注意,发送 None
实际上等同于调用 gen.__next__()
或 next(gen)
)。但是你没有尝试第二次发送值,所以在那种情况下没有 StopIteration
。生成器只是停在那里,在 yield 语句处暂停,等待您再次点击它,这也是您之后还没有看到打印的原因。
另一点需要注意的是,如果您在第二个示例中发送了 None
以外的任何内容,则会出现错误:
TypeError: can't send non-None value to a just-started generator
这就是我在 'priming' 协程中所说的。
行为没有不同;在第二个设置中,您从未超越生成器中的第一个 yield
表达式。请注意 StopIteration
是 而不是错误 ;这是正常行为,只要生成器结束就会触发预期的信号。在您的第二个示例中,您从未到达生成器的末尾。
每当生成器到达 yield
表达式时,执行会暂停 就在那里 ,表达式在恢复之前无法在生成器中生成任何内容。 gen.__next__()
或 gen.send()
都将从该点恢复执行,yield
表达式生成 gen.send()
或 None
传入的值。如果有帮助,您可以将 gen.__next__()
视为 gen.send(None)
。这里要意识到的一件事是 gen.send()
有 yield
return 发送值 first,then 生成器继续到下一个 yield
.
因此,给定您的第一个示例生成器,会发生这种情况:
gen = send_gen()
创建生成器对象。代码暂停在函数的最顶部,什么都不执行。你要么叫
gen.__next__()
要么叫gen.send(None)
;生成器开始并执行直到第一个yield
表达式:print(" send_gen(): will yield 1") yield 1
现在执行暂停。
gen.__next__()
或gen.send(None)
现在调用 return1
,yield 1
产生的值。因为生成器现在已暂停,所以x = ...
赋值还不能发生!只有当发电机再次恢复时才会发生这种情况。您在第一个示例中调用
gen.send("a string")
,不要在第二个示例中调用 any。所以对于第一个例子,现在恢复生成器函数:x = <return value of the yield expression> # 'a string' in this case print(" send_gen(): sent in '{}'".format(x))
现在函数 结束 ,所以
StopIteration
被引发。
因为您从未在第二个示例中恢复生成器,所以未到达生成器的末尾并且没有引发 StopIteration
异常。
请注意,因为生成器从函数的顶部开始,所以在那个点上没有 yield
表达式到 return 无论你用 gen.send()
发送什么,所以第一个 gen.send()
值必须始终为 None
否则会引发异常。最好对生成器 'prime' 使用显式 gen.__next__()
(或者更确切地说 next(gen)
函数调用),这样它就会在第一个 yield
表达式处暂停。
从技术上讲,这是一个协程而不是生成器:
- 当我们调用send_gen()时,协程对象被创建
- 当调用 gen.___next____() 时 [我们应该使用 next(gen) ],生成器函数被调用 returns 1 并阻塞
- 当gen.send("a string")被调用时,协程被唤醒并处理输入(这里打印"a string")
- 然后协程退出