Answers:
您可以return
在生成器中使用一次;它会停止迭代而不会产生任何结果,因此提供了一种使函数超出范围的明确选择。因此,用于yield
将函数转换为生成器,但在return
产生任何内容之前先终止该生成器。
>>> def f():
... return
... yield
...
>>> list(f())
[]
我不确定这是否比您拥有的要好得多-它只是将无操作if
语句替换为无操作yield
语句。但这更惯用了。请注意,仅使用yield
不起作用。
>>> def f():
... yield
...
>>> list(f())
[None]
iter(())
?这个问题专门询问一个空的生成器函数。因此,我将其视为关于Python语法的内部一致性的问题,而不是一般而言有关创建空迭代器的最佳方法的问题。
如果问题实际上是关于创建一个空迭代器的最佳方法,那么您可能同意Zectbumo关于使用它的iter(())
替代方法。但是,请务必注意iter(())
不要返回函数!它直接返回一个空的Iterable。假设您正在使用一个期望可调用返回一个可迭代对象的API 。您必须执行以下操作:
def empty():
return iter(())
(信用额应转到Unutbu,以给出此答案的第一个正确版本。)
现在,您可能会发现上面的内容更加清晰,但是我可以想象一下情况不太清楚的情况。考虑以下(伪造的)生成器函数定义列表的示例:
def zeros():
while True:
yield 0
def ones():
while True:
yield 1
...
在这个长长的列表的末尾,我宁愿看到其中包含yield
的内容,如下所示:
def empty():
return
yield
或者,在Python 3.3及更高版本中(如DSM所建议的那样):
def empty():
yield from ()
对存在yield
关键字清楚地在最短的一瞥,这只是另一个发生器功能,所有其他的一模一样。花更多的时间才能看到该iter(())
版本正在执行相同的操作。
这是一个细微的差异,但是老实说,我认为yield
基于功能的函数更具可读性和可维护性。
itertools.empty()
。
return
意味着在生成器内部有些不同。更像是break
。
False
。
iter(())
您不需要发电机。来吧!
iter([])
一个简单的事实更具吸引力,因为事实()
是一个常数,而[]
每次调用它可能会在内存中实例化一个新的列表对象。
empty = lambda: iter(())
或的东西def empty(): return iter(())
。
Python 3.3(因为我yield from
踢了,因为@senderle偷走了我的第一个念头):
>>> def f():
... yield from ()
...
>>> list(f())
[]
但是我不得不承认,我很难为此提出一个用例,iter([])
否则用例(x)range(0)
就不能很好地解决问题。
return; yield
或更具可读性if False: yield None
。
iter([])
或者用例(x)range(0)
不能很好地解决。” ->不确定是什么(x)range(0)
,但用例可以是某些继承类中的完整生成器所覆盖的方法。出于一致性的目的,您甚至希望其他继承者继承的基础生成器也像覆盖它们的生成器一样返回生成器。
另一个选择是:
(_ for _ in ())
生成空迭代器的“标准”方法似乎是iter([])。我建议将[]设为iter()的默认参数;这与良好的论据被拒绝,见http://bugs.python.org/issue25215 - Jurjen
就像@senderle所说的,使用这个:
def empty():
return
yield
我写这个答案主要是为了分享另一个理由。
选择此解决方案优先于其他解决方案的一个原因是,对于解释器而言,它是最佳的。
>>> 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
如我们所见,的empty_return
字节码与常规的空函数完全相同;其余的执行许多其他操作,这些操作无论如何都不会改变行为。empty_return
和之间的唯一区别noop
是,前者已设置了generator标志:
>>> 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
当然,该论点的强度非常依赖于所使用的Python的特定实现;具体请参见第5章。一个足够聪明的替代解释器可能会注意到其他操作毫无用处,并对其进行了优化。但是,即使存在这种优化,它们也需要解释器花费时间执行优化,并防止优化假设被破坏,例如iter
全局范围内的标识符反弹到其他事物(即使这很可能表示错误,如果它实际上发生了)。在empty_return
没有什么可以优化的情况下,因此即使是天真的CPython也不会在任何虚假操作上浪费时间。
generator = (item for item in [])
if False: yield
不知道这种模式的人来说,这确实好于但仍然有些困惑