5

假设我正在循环一个可迭代对象,如果迭代器为空,我想采取一些措施。我能想到的两种最好的方法是:

for i in iterable:
     # do_something
if not iterable:
    # do_something_else

empty = True
for i in iterable:
    empty = False
    # do_something
if empty:
    # do_something_else

第一个取决于可迭代对象是一个集合(当可迭代对象被传递到循环所在的函数/方法时无用),第二个集合empty在每次通过循环时设置,这看起来很丑陋。

还有另一种我想念的方法还是第二种选择是最好的?如果有一些子句可以添加到循环语句中来为我处理这个问题,就像elsenot_found标志消失一样,那将是非常酷的。


我不是在寻找聪明的黑客。

我不是在寻找涉及大量代码的解决方案

我正在寻找一个简单的语言功能。我正在寻找一种清晰Python的方式来迭代一个可迭代对象,并在可迭代对象为空时采取一些操作,任何有经验的 Python 程序员都会理解。如果我可以在每次迭代中不设置标志的情况下做到这一点,那就太棒了。如果没有简单的成语可以做到这一点,那就忘了它。

4

8 回答 8

3

这是相当骇人听闻的,但您可以删除i它,然后在循环之后检查它是否存在(如果不存在,则循环从未发生过):

try:
    del i
except NameException: pass

for i in iterable:
    do_something(i)

try:
    del i
except NameException:
    do_something_else()

我认为这可能比仅使用标志更丑陋

于 2010-08-15T05:07:41.007 回答
3

我认为这是最干净的方法:

# first try with exceptions
def nonempty( iter ):
    """ returns `iter` if iter is not empty, else raises TypeError """
    try:
        first = next(iter)
    except StopIteration:
        raise TypeError("Emtpy Iterator")
    yield first
    for item in iter:
        yield item


# a version without exceptions. Seems nicer:
def isempty( iter ):
    """ returns `(True, ())` if `iter` if is empty else `(False, iter)`
         Don't use the original iterator! """
    try:
        first = next(iter)
    except StopIteration:
        return True, ()
    else:
        def iterator():
            yield first
            for item in iter:
                yield item
        return False, iterator()



for x in ([],[1]):
    # first version
    try:
        list(nonempty(iter(x))) # trying to consume a empty iterator raises
    except TypeError:
        print x, "is empty"
    else:
        print x, "is not empty"

    # with isempty
    empty, it = isempty(iter(x))
    print x,  "is", ("empty" if empty else "not empty")
于 2010-08-15T17:39:04.947 回答
2

更新 2

我喜欢Odomontois 的回答。恕我直言,它比我在下面写的更适合这个问题。

更新

(在阅读了 OP 的评论和编辑过的问题之后)你也可以这样做。见下文:

def with_divisible(n, a, b, f):
 it = (i for i in xrange(a, b) if not i % n)
 for i in wrapper(it):
  f(i)

>>> with_divisible(1, 1, 1, lambda x: x)
Traceback (most recent call last):
  File "<pyshell#55>", line 1, in <module>
    with_divisible(1, 1, 1, lambda x: x)
  File "<pyshell#54>", line 3, in with_divisible
    for i in wrapper(it):
  File "<pyshell#46>", line 4, in wrapper
    raise EmptyIterableException("Empty")
EmptyIterableException: Empty

>>> with_divisible(7, 1, 21, lambda x: x)
7
14
...Snipped...
    raise EmptyIterableException("Empty")
EmptyIterableException: Empty

原始答案

有趣的问题。我做了一些实验,得出以下结论:

class EmptyIterableException(Exception):
    pass

def wrapper(iterable):
    for each in iterable:
        yield each
    raise EmptyIterableException("Empty")

try:
    for each in wrapper(iterable):
        do_something(each)
except EmptyIterableException, e:
    do_something_else()
于 2010-08-15T05:58:48.100 回答
2
if not map(do_something_callable,iterable) : 
    # do something else
于 2010-08-15T07:25:39.313 回答
1

如果要在使用之前对迭代器进行部分检查,一般的前进方式是使用itertools.tee. 这样,我们可以拥有两个迭代器副本并检查一个是否为空,同时仍然从一开始就使用另一个副本。

from itertools import tee
it1, it2 = tee(iterable)
try:
    it1.next()
    for i in it2:
        do_some_action(i) #iterator is not empty
except StopIteration:
    do_empty_action() #iterator is empty

StopIteration异常必然是调用的结果,it1.next()因为在循环内引发的任何StopIteration异常都将终止该循环。

编辑:对于那些不喜欢此类异常的人,islice可用于设置单步循环:

from itertools import tee, islice
it1, it2 = tee(iterable)
for _ in islice(it1, 1):
    #loop entered if iterator is not empty
    for i in it2:
        do_some_action(i)
    break #if loop entered don't execute the else section
else:
    do_empty_action()

我个人更喜欢第一种风格。YMMV。

于 2010-08-15T18:11:19.807 回答
0

颠倒“if”和“for”怎么样:

if iterable:
    for i in iterable:
        do_something(i)
else:
    do_something_else()

好的,这不起作用!

这是另一个解决方案:http ://code.activestate.com/recipes/413614-testing-for-an-empty-iterator/

于 2010-08-15T06:55:29.367 回答
0

这是Michael MrozekFM答案的组合:

def with_divisible(n, a, b, f):
    '''apply f to every integer x such that n divides x and a <= x < b'''
    it = (i for i in xrange(a, b) if not i % n)
    for i in it:
        f(i)
    try: i            # test if `it` was empty
    except NameError: print('do something else')

def g(i):
    print i,

with_divisible( 3, 1, 10, g)   # Prints 3 6 9.
with_divisible(33, 1, 10, g)   # Prints "do something else"
于 2010-08-15T15:38:06.057 回答
0

生成器有一个 'gi_frame' 属性,一旦生成器耗尽,该属性为 None ,但仅在引发 StopIteration 之后。如果这是可以接受的,您可以尝试以下方法:

import types

def do(x, f, f_empty):
    if type(x) == types.GeneratorType:
        # generators have a 'gi_frame' property,
        # which is None once the generator is exhausted
        if x.gi_frame:
            # not empty
            return f(x)
        return f_empty(x)
    if x:
        return f(x)
    return f_empty(x)

def nempty(lst):
    print lst, 'not empty'

def empty(lst):
    print 'Twas empty!'

# lists
do([2,3,4], nempty, empty)
do([], nempty, empty)

# generators
do((i for i in range(5)), nempty, empty)
gen = (i for i in range(1))
gen.next()
try:
    gen.next()
except StopIteration:
    pass
do(gen, nempty, empty)
于 2010-08-15T17:44:56.953 回答