在python中,可以通过将yield关键字放在函数体中来轻松定义迭代器函数,例如:
def gen():
for i in range(100):
yield i
Run Code Online (Sandbox Code Playgroud)
如何定义不产生值的生成器函数(生成0值),以下代码不起作用,因为python不能知道它应该是生成器而不是正常函数:
def empty():
pass
Run Code Online (Sandbox Code Playgroud)
我可以做点什么
def empty():
if False:
yield None
Run Code Online (Sandbox Code Playgroud)
但那会非常难看.有没有很好的方法来实现一个空的迭代器函数?
sen*_*rle 114
你可以return在发电机中使用一次; 它会停止迭代而不会产生任何东西,因此提供了一个明确的替代方法,让函数超出范围.因此,使用yield将函数转换为生成器,但在它之前return用于在生成任何内容之前终止生成器.
>>> def f():
... return
... yield
...
>>> list(f())
[]
Run Code Online (Sandbox Code Playgroud)
我不确定它比你拥有的要好得多 - 它只是用无操作if语句取代了无操作yield语句.但它更具惯用性.请注意,只是使用yield不起作用.
>>> def f():
... yield
...
>>> list(f())
[None]
Run Code Online (Sandbox Code Playgroud)
iter(())?这个问题具体询问空发生器功能.出于这个原因,我认为它是关于Python语法的内部一致性的问题,而不是关于一般创建空迭代器的最佳方法的问题.
如果问题实际上是关于创建空迭代器的最佳方法,那么您可能会同意Zectbumo关于使用它iter(()).但是,重要的是观察iter(())不返回功能!它直接返回一个空的iterable.假设您正在使用期望可返回可迭代的可调用的API .你必须做这样的事情:
def empty():
return iter(())
Run Code Online (Sandbox Code Playgroud)
(信用证应该转到Unutbu,以提供此答案的第一个正确版本.)
现在,你可能会发现上面更清楚,但我可以想象它不太清楚的情况.考虑这个(人为的)生成器函数定义列表的示例:
def zeros():
while True:
yield 0
def ones():
while True:
yield 1
...
Run Code Online (Sandbox Code Playgroud)
在那个长列表的末尾,我宁愿看到其中的yield内容,如下所示:
def empty():
return
yield
Run Code Online (Sandbox Code Playgroud)
或者,在Python 3.3及以上版本中(如DSM所建议),这个:
def empty():
yield from ()
Run Code Online (Sandbox Code Playgroud)
yield关键字的存在使得它在最简洁的一瞥中清楚地表明这只是另一个生成器功能,与所有其他功能完全一样.需要花费更多的时间才能看到该iter(())版本正在做同样的事情.
这是一个微妙的差异,但老实说,我认为yield基于功能的功能更具可读性和可维护性.
Zec*_*umo 63
iter(())
Run Code Online (Sandbox Code Playgroud)
你不需要发电机.来吧伙计们!
DSM*_*DSM 46
Python 3.3(因为我很开心yield from,因为@senderle偷走了我的第一个想法):
>>> def f():
... yield from ()
...
>>> list(f())
[]
Run Code Online (Sandbox Code Playgroud)
但我不得不承认,我很难想出一个用于此的用例,iter([])或者(x)range(0)不能同样好用.
Ben*_*war 14
另一种选择是:
(_ for _ in ())
Run Code Online (Sandbox Code Playgroud)
我更喜欢以下内容:
def foo():
raise StopIteration()
yield
Run Code Online (Sandbox Code Playgroud)
"yield"将其转换为生成器,而Exception意味着None不包含在结果中(纯粹的空结果).
就像@senderle 说的,使用这个:
def empty():
return
yield
Run Code Online (Sandbox Code Playgroud)
我写这个答案主要是为了分享另一个理由。
选择此解决方案优于其他解决方案的一个原因是,就解释器而言,它是最佳的。
>>> import dis
>>> def empty_yield_from():
... yield from ()
...
>>> def empty_iter():
... return iter(())
...
>>> def empty_return():
... return
... yield
...
>>> def noop():
... pass
...
>>> dis.dis(empty_yield_from)
2 0 LOAD_CONST 1 (())
2 GET_YIELD_FROM_ITER
4 LOAD_CONST 0 (None)
6 YIELD_FROM
8 POP_TOP
10 LOAD_CONST 0 (None)
12 RETURN_VALUE
>>> dis.dis(empty_iter)
2 0 LOAD_GLOBAL 0 (iter)
2 LOAD_CONST 1 (())
4 CALL_FUNCTION 1
6 RETURN_VALUE
>>> dis.dis(empty_return)
2 0 LOAD_CONST 0 (None)
2 RETURN_VALUE
>>> dis.dis(noop)
2 0 LOAD_CONST 0 (None)
2 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
正如我们所看到的,empty_return具有与常规空函数完全相同的字节码;其余的执行许多其他操作,无论如何都不会改变行为。empty_return和之间的唯一区别noop是前者设置了生成器标志:
>>> dis.show_code(noop)
Name: noop
Filename: <stdin>
Argument count: 0
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 1
Flags: OPTIMIZED, NEWLOCALS, NOFREE
Constants:
0: None
>>> dis.show_code(empty_return)
Name: empty_return
Filename: <stdin>
Argument count: 0
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 1
Flags: OPTIMIZED, NEWLOCALS, GENERATOR, NOFREE
Constants:
0: None
Run Code Online (Sandbox Code Playgroud)
当然,这个论点的强弱很大程度上取决于所使用的 Python 的特定实现;一个足够聪明的替代解释器可能会注意到其他操作毫无用处并优化它们。然而,即使存在这样的优化,它们也需要解释器花时间来执行它们并防止优化假设被破坏,例如iter全局范围内的标识符被反弹到其他东西(即使这很可能表明存在错误,如果它确实发生过)。在empty_return没有什么可以优化的情况下,即使是相对幼稚的 CPython 也不会在任何虚假操作上浪费时间。
| 归档时间: |
|
| 查看次数: |
28391 次 |
| 最近记录: |