简体   繁体   English

在Python生成器中使用for…else

[英]Using for…else in Python generators

I'm a big fan of Python's for...else syntax - it's surprising how often it's applicable, and how effectively it can simplify code. 我是Python的... else语法的忠实拥护者 -令人惊讶的是它的应用频率以及简化代码的有效性。

However, I've not figured out a nice way to use it in a generator, for example: 但是,我还没有找到在生成器中使用它的好方法,例如:

def iterate(i):
    for value in i:
        yield value
    else:
        print 'i is empty'

In the above example, I'd like the print statement to be executed only if i is empty. 在上面的示例中,我希望仅在i为空时才执行print语句。 However, as else only respects break and return , it is always executed, regardless of the length of i . 然而,随着else只有方面breakreturn ,它总是执行的长度无关, i

If it's impossible to use for...else in this way, what's the best approach to this so that the print statement is only executed when nothing is yielded? 如果无法以这种方式for...else ,那么最好的方法是什么,以便仅在不产生任何结果时才执行print语句?

You're breaking the definition of a generator, which should throw a StopIteration exception when iteration is complete (which is automatically handled by a return statement in a generator function) 您正在破坏生成器的定义,该定义应在迭代完成时引发StopIteration异常(生成器函数中的return语句会自动处理该异常)

So: 所以:

def iterate(i):
    for value in i:
        yield value
    return

Best to let the calling code handle the case of an empty iterator: 最好让调用代码处理空迭代器的情况:

count = 0
for value in iterate(range([])):
    print value
    count += 1
else:
    if count == 0:
        print "list was empty"

Might be a cleaner way of doing the above, but that ought to work fine, and doesn't fall into any of the common 'treating an iterator like a list' traps below. 可能是做上述事情的一种更清洁的方法,但是应该可以正常工作,并且不属于下面常见的任何“像列表一样处理迭代器”陷阱。

There are a couple ways of doing this. 有两种方法可以做到这一点。 You could always use the Iterator directly: 您总是可以直接使用Iterator

def iterate(i):
    try:
        i_iter = iter(i)
        next = i_iter.next()
    except StopIteration:
        print 'i is empty'
        return

    while True:
        yield next
        next = i_iter.next()

But if you know more about what to expect from the argument i , you can be more concise: 但是,如果您对参数i期望的了解更多,则可以更加简洁:

def iterate(i):
    if i:  # or if len(i) == 0
        for next in i:
            yield next
    else:
        print 'i is empty'
        raise StopIteration()

As you note, for..else only detects a break . 如您所述, for..else仅检测到break So it's only applicable when you look for something and then stop . 因此,它仅在您寻找某物然后停止时才适用。

It's not applicable to your purpose not because it's a generator, but because you want to process all elements, without stopping (because you want to yield them all, but that's not the point). 它不是适用于您的目的,不是因为它是生成器,而是因为您想处理所有元素而不停止 (因为您想产生所有元素 ,但这不是重点)。

So generator or not, you really need a boolean, as in Ber's solution. 不管是否生成器,就像Ber的解决方案中一样,您确实需要布尔值。

Summing up some of the earlier answers, it could be solved like this: 总结一些较早的答案,可以这样解决:

def iterate(i):
    empty = True
    for value in i:
        yield value
        empty = False

    if empty:
        print "empty"

so there really is no "else" clause involved. 因此,实际上没有涉及“ else”子句。

If it's impossible to use for...else in this way, what's the best approach to this so that the print statement is only executed when nothing is yielded? 如果无法以这种方式用于... else,那么最好的方法是什么,以便仅在不产生任何结果时才执行print语句?

Maximum i can think of: 我能想到的最大值:


>>> empty = True
>>> for i in [1,2]:
...     empty = False
... if empty:
...     print 'empty'
...
>>>
>>>
>>> empty = True
>>> for i in []:
...     empty = False
... if empty:
...    print 'empty'
...
empty
>>>

What about simple if-else? 简单的if-else呢?

def iterate(i):
    if len(i) == 0: print 'i is empty'
    else:
        for value in i:
            yield value

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM