在python中,可以通过将yield关键字放在函数体中来轻松定义迭代器函数,例如:
def gen():
for i in range(100):
yield i
如何定义不产生值的生成器函数(生成0值),以下代码不起作用,因为python不能知道它应该是生成器而不是普通函数:
def empty():
pass
我可以做类似
的事情def empty():
if False:
yield None
但那会非常难看。有没有很好的方法来实现一个空的迭代器函数?
答案 0 :(得分:101)
您可以在生成器中使用return
一次;它会停止迭代而不会产生任何东西,因此提供了一个明确的替代方法,让函数超出范围。因此,使用yield
将函数转换为生成器,但在其前面加return
以在生成任何内容之前终止生成器。
>>> def f():
... return
... yield
...
>>> list(f())
[]
我不确定它比你拥有的要好得多 - 它只是用no-op if
语句替换了no-op 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
的函数更具可读性和可维护性。
答案 1 :(得分:61)
iter(())
您不需要生成器。来吧!
答案 2 :(得分:43)
Python 3.3(因为我正在yield from
踢,因为@senderle偷走了我的第一个想法):
>>> def f():
... yield from ()
...
>>> list(f())
[]
但我不得不承认,我很难想出一个用例,因为iter([])
或(x)range(0)
不会同样有效。
答案 3 :(得分:12)
另一种选择是:
(_ for _ in ())
答案 4 :(得分:6)
我更喜欢以下内容:
def foo():
raise StopIteration()
yield
"产量"将它变成生成器,而异常意味着结果中不包含任何内容(纯粹的空结果)。
答案 5 :(得分:4)
它必须是发电机功能吗?如果没有,那怎么样
def f():
return iter([])
答案 6 :(得分:3)
制作空迭代器的“标准”方法似乎是iter([])。 我建议将[]设为默认参数iter();这被驳回了很好的论据,请参阅http://bugs.python.org/issue25215 - Jurjen
答案 7 :(得分:3)
就像@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
之间的唯一区别是,前者已设置了生成器标志:
>>> 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的特定实现。一个足够聪明的替代解释器可能会注意到其他操作毫无用处,并对其进行了优化。但是,即使存在此类优化,它们也需要解释器花费时间执行优化,并防止优化假设被破坏,例如全局范围内的iter
标识符反弹到其他事物(即使这很可能会指出一个错误(如果它确实发生了)。在empty_return
的情况下,根本没有什么值得优化的,因此,即使是相对天真的CPython也不会在任何虚假操作上浪费时间。
答案 8 :(得分:0)
generator = (item for item in [])
答案 9 :(得分:0)
对于那些真正需要一个功能而且实际上需要一个发电机的人
empty = lambda: (_ for _ in ())
答案 10 :(得分:0)
由于我们还没有任何建议,因此我想举一个基于类的示例。这是一个不生成任何项目的可调用迭代器。我相信这是解决问题的一种简单明了的方式。
class EmptyGenerator:
def __iter__(self):
return self
def __next__(self):
raise StopIteration
>>> list(EmptyGenerator())
[]