代码之家  ›  专栏  ›  技术社区  ›  aaronasterling

在试图循环一个空的iterable时采取行动的惯用方法

  •  5
  • aaronasterling  · 技术社区  · 14 年前

    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
    

    第一个依赖于iterable是一个集合(当iterable被传递到循环所在的函数/方法中时就没有用了),第二个依赖于iterable是一个集合 empty 在每一个看起来很难看的循环中。

    有没有别的方法让我错过了,或者第二种方法是最好的?如果有一个子句可以添加到循环语句中,让我像 else 制造 not_found


    我不是在找聪明的黑客。

    我正在寻找一个简单的语言功能。 我在找一个 清楚的 蟒蛇 如果没有一个简单的习语能做到这一点,那就忘掉它吧。

    8 回复  |  直到 10 年前
        1
  •  3
  •   Jochen Ritzel    14 年前

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

    # 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")
    
        2
  •  3
  •   Michael Mrozek    14 年前

    这是相当黑客,但你可以删除 i

    try:
        del i
    except NameException: pass
    
    for i in iterable:
        do_something(i)
    
    try:
        del i
    except NameException:
        do_something_else()
    

    我想这可能比仅仅用旗子更难看

        3
  •  2
  •   Community CDub    7 年前

    更新2

    我喜欢 Odomontois' answer . 我觉得它比我下面写的更适合这个问题。

    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()
    
        4
  •  2
  •   Odomontois    14 年前
    if not map(do_something_callable,iterable) : 
        # do something else
    
        5
  •  1
  •   Muhammad Alkarouri    14 年前

    如果在使用迭代器之前要部分检查迭代器,一般的方法是使用 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() 停止迭代

    编辑 :对于那些不喜欢这种例外的人, 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()
    

        6
  •  0
  •   Eike    14 年前

    把“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/

        7
  •  0
  •   Community CDub    7 年前

    这是 Michael Mrozek 的和 FM 的答案:

    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"
    
        8
  •  0
  •   nilamo    14 年前

    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)