如何让Python将函数识别为生成器函数?

时间:2016-05-18 15:15:19

标签: python generator 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中所述,“最后,该提案还清除了有关如何终止生成器的混淆:正确的方法是return,而不是提升StopIteration。 “,我只得到:

maingen started

(没有maingen finished ...)

我发现让事情变得好的唯一方法是:

>>> def subgen_4(i):
...     i * 3
...     return
...     yield
... 

有了这个,我得到:

maingen started
maingen finished

Hourrah!但这个解决方案并不美观......

有没有人有更好或更多的pythonic想法?

是否可以编写一个装饰器来秘密添加丑陋的yield语句?

2 个答案:

答案 0 :(得分:2)

如果您希望子生成器不产生任何内容,则返回一个空迭代器:

def subgen_2(i):
    i * 3
    return iter([]) #empty iterator

您获得TypeError: 'NoneType' object is not iterable的原因是因为在您的子生成器中没有yield语句,它不是生成器,而是隐式返回None的常规函数​​。

通过返回一个没有生成任何值的有效迭代器,您将能够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

答案 1 :(得分:0)

阅读完所有评论后,我认为更多 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生成器函数中“注入”时都被“接受”,并且不需要一些丑陋的yield语句。