如何让 Python 将函数识别为生成器函数?
How to make Python recognize a function as a generator function?
首先,考虑下面的代码(接下来我将讨论subgen()
的几个版本):
>>> def maingen(i):
... print("maingen started")
... yield from subgen(i)
... print("maingen finished")
...
>>> for i in maingen(5):
... print(i)
...
我想写几个 subgen
生成器函数。
正常的是:
>>> def subgen_1(i):
... yield i + 1
... yield i + 2
... yield i + 3
没有问题,输出符合预期:
maingen started
6
7
8
maingen finished
现在,我想要另一个版本的 subgen
,它不会产生任何结果...
如果我尝试这样做:
>>> def subgen_2(i):
... i * 3
我有一个例外:
maingen started
Traceback (most recent call last):
...
TypeError: 'NoneType' object is not iterable
预计:subgen_2
不是生成器函数。
好的,下一个。我可以在某个地方阅读(比如第一个答案here)
我必须提出 StopIteration
。 (请注意,作为新手,我无法评论此答案。)
>>> def subgen_3(i):
... i * 3
... raise StopIteration()
...
如 PEP 479、"Finally, the proposal also clears up the confusion about how to terminate a generator: the proper way is return
, not raise StopIteration
." 中所述,我只得到:
maingen started
(没有maingen finished
...)
我发现让一切顺利的唯一方法是:
>>> def subgen_4(i):
... i * 3
... return
... yield
...
有了这个,我得到:
maingen started
maingen finished
万岁!但是这个解决方案并不漂亮...
有没有人有更好或更 pythonic 的想法?
能不能写个装饰器偷偷加丑陋的yield语句?
如果您希望子生成器不产生任何结果,那么return一个空迭代器:
def subgen_2(i):
i * 3
return iter([]) #empty iterator
你得到 TypeError: 'NoneType' object is not iterable
的原因是因为在你的子生成器中没有 yield
语句它不是生成器,而是一个隐式 returned None
.
通过 return 一个不产生任何值的有效迭代器,您将能够 yield from subgen_2()
没有错误并且不会产生任何额外的值。
隐藏它的另一种方法(我真的不明白你为什么要这样做)是制作一个装饰器,它实际上只是调用你的函数然后 return iter(())
或 yield from []
.
def gen_nothing(f):
@functools.wraps(f)
def wrapper(*args,**kw):
f(*args,**kw)
yield from []
return wrapper
但是这产生的唯一区别是堆栈将需要一个额外的包装帧,这意味着您的 Traceback 消息将有更多噪音:
Traceback (most recent call last):
File "/Users/Tadhg/Documents/codes/test.py", line 15, in <module>
for i in subgen():
File "/Users/Tadhg/Documents/codes/test.py", line 6, in wrapper
f(*args,**kw)
File "/Users/Tadhg/Documents/codes/test.py", line 12, in subgen
3/0
ZeroDivisionError: division by zero
看完所有评论,我觉得更pythonic的方法是重写maingen
:
def maingen(i):
print("maingen started")
gen = subgen(i)
if gen:
yield from gen
print("maingen finished")
这样,一个 subgen
实际上是一个生成器函数:
def subgen_1(i):
yield i + 1
yield i + 2
yield i + 3
或 subgen
这是一个简单的函数:
def subgen_2(i):
i * 3
在 maingen
生成器函数中 "injected" 时都是 "accepted",不需要一些丑陋的 yield
语句。
首先,考虑下面的代码(接下来我将讨论subgen()
的几个版本):
>>> def maingen(i):
... print("maingen started")
... yield from subgen(i)
... print("maingen finished")
...
>>> for i in maingen(5):
... print(i)
...
我想写几个 subgen
生成器函数。
正常的是:
>>> def subgen_1(i):
... yield i + 1
... yield i + 2
... yield i + 3
没有问题,输出符合预期:
maingen started
6
7
8
maingen finished
现在,我想要另一个版本的 subgen
,它不会产生任何结果...
如果我尝试这样做:
>>> def subgen_2(i):
... i * 3
我有一个例外:
maingen started
Traceback (most recent call last):
...
TypeError: 'NoneType' object is not iterable
预计:subgen_2
不是生成器函数。
好的,下一个。我可以在某个地方阅读(比如第一个答案here)
我必须提出 StopIteration
。 (请注意,作为新手,我无法评论此答案。)
>>> def subgen_3(i):
... i * 3
... raise StopIteration()
...
如 PEP 479、"Finally, the proposal also clears up the confusion about how to terminate a generator: the proper way is return
, not raise StopIteration
." 中所述,我只得到:
maingen started
(没有maingen finished
...)
我发现让一切顺利的唯一方法是:
>>> def subgen_4(i):
... i * 3
... return
... yield
...
有了这个,我得到:
maingen started
maingen finished
万岁!但是这个解决方案并不漂亮...
有没有人有更好或更 pythonic 的想法?
能不能写个装饰器偷偷加丑陋的yield语句?
如果您希望子生成器不产生任何结果,那么return一个空迭代器:
def subgen_2(i):
i * 3
return iter([]) #empty iterator
你得到 TypeError: 'NoneType' object is not iterable
的原因是因为在你的子生成器中没有 yield
语句它不是生成器,而是一个隐式 returned None
.
通过 return 一个不产生任何值的有效迭代器,您将能够 yield from subgen_2()
没有错误并且不会产生任何额外的值。
隐藏它的另一种方法(我真的不明白你为什么要这样做)是制作一个装饰器,它实际上只是调用你的函数然后 return iter(())
或 yield from []
.
def gen_nothing(f):
@functools.wraps(f)
def wrapper(*args,**kw):
f(*args,**kw)
yield from []
return wrapper
但是这产生的唯一区别是堆栈将需要一个额外的包装帧,这意味着您的 Traceback 消息将有更多噪音:
Traceback (most recent call last):
File "/Users/Tadhg/Documents/codes/test.py", line 15, in <module>
for i in subgen():
File "/Users/Tadhg/Documents/codes/test.py", line 6, in wrapper
f(*args,**kw)
File "/Users/Tadhg/Documents/codes/test.py", line 12, in subgen
3/0
ZeroDivisionError: division by zero
看完所有评论,我觉得更pythonic的方法是重写maingen
:
def maingen(i):
print("maingen started")
gen = subgen(i)
if gen:
yield from gen
print("maingen finished")
这样,一个 subgen
实际上是一个生成器函数:
def subgen_1(i):
yield i + 1
yield i + 2
yield i + 3
或 subgen
这是一个简单的函数:
def subgen_2(i):
i * 3
在 maingen
生成器函数中 "injected" 时都是 "accepted",不需要一些丑陋的 yield
语句。