【问题标题】:Python execute code only if for loop did not begin iteration (with generator)?仅当 for 循环没有开始迭代(使用生成器)时,Python 才执行代码?
【发布时间】:2013-08-09 08:09:51
【问题描述】:

for/else 子句中的else 块在迭代完成但未被breakso I read 中断时执行。

是否有一种语言结构可以让我编写仅在for 循环没有开始迭代时才执行的东西?如果我使用的是tuplelist,我会这样做:

if seq:
    for x in seq:
         # something
else:
    # something else

但是当我使用生成器时,我没有得到我想要的行为:

>>> g = (x for x in range(2))
>>> for x in g:
...     print x
... else:
...     print "done"
... 
0
1
done    # I don't want "done" here
>>> g = (x for x in range(2) if x > 1)
>>> if g:
...     for x in g:
...         print x
... else:
...     print "done"
... 
>>>     # I was expecting "done" here

我怎样才能做到这一点,而不用从生成器创建tuplelist,同时还使用for 循环?我可以在while 循环中使用next() 并尝试捕获StopIteration,但我想看看是否有一个很好的方法来使用for

【问题讨论】:

  • 我可能会在循环内设置一个ran 标志并使用if not ran:
  • @Ludo 我知道我不知道生成器一开始是否为空。我只是想知道是否有一个很好的语言结构来处理这种情况。

标签: python python-2.7 generator


【解决方案1】:

我想不出比在 for 循环中更新布尔值更好的方法了。

any_results = False
for x in g:
    any_results = True
    print x
if not any_results:
    print 'Done'

【讨论】:

  • 是的,我通常会这样做;我只是想知道是否有一个控制结构或其他语言结构可以简洁地做到这一点。
  • 这就像有一个计数器。我们不能用python提供的for...else吗?
  • @shadow0359 - 我不明白,for...else 在一个空的g 上会以true 退出。
【解决方案2】:

我认为了解循环是否实际执行的一个好方法是使用循环变量

lv= 1
for x in g:
    lv = lv+1
    print x
if (lv == 1):
    print 'Done'

我的语法可能是错误的,因为我不是 python 人..

【讨论】:

    【解决方案3】:

    您可以使用生成器函数:

    next 接受可选的第二个参数,可用于在迭代器耗尽时指定默认值。

    def func(lis):
        g = (x for x in lis if x > 1)
        flag = object()      # expected to be unique
        nex = next(g, flag)  # will return flag if genexp is empty
        if nex is not flag:
            yield nex
            for item in g:
                yield item
        else:
            yield "done"
    
    for x in func(range(2)):
        print x
    print "------"
    for x in func(range(4)):
        print x
    

    输出:

    done
    ------
    2
    3
    

    【讨论】:

    • 这在内存效率和执行时间方面与布尔方法或tuple(g) 相比如何?
    • @2rs2ts 由于迭代器有一些额外的开销,因此与布尔方法相比它们会很慢,但优点是您可以使用 itertools.islice 访问有限数量的项目(即切片),而无需迭代所有项目(如果在任何情况下都需要),你不能用一个简单的for循环来做到这一点。阅读this 以比较元组/列表和 genexp。
    • 非常酷。我现在将更频繁地使用列表推导来处理较小的数据集。无论如何,出于我的目的,因为我不需要任何切片或其他形式的迭代,我将使用布尔方法。但是在您的回答和您的评论之间,这非常有用。谢谢!
    【解决方案4】:
    n = -1
    for n, i in enumerate(it):
        do_stuff()
    if n < 0:
        print 'Done'
    

    【讨论】:

    • 我喜欢使用enumerate
    【解决方案5】:

    在此处的示例中,您需要额外的构造吗?

    caught = None
    for item in x:
        caught = item
        print caught
    if caught != None: print "done"
    

    *为 OP 评论编辑*t

    【讨论】:

    • 不,我希望它只打印"done",如果它没有任何东西可以迭代。
    【解决方案6】:

    您可以编写一个计算迭代次数的包装器。它的优点是可以处理更多奇特的枚举。在 python3 中,它会是这样的:

    import sys
    from glob import iglob
    
    class GenCount(object):
    
        def __init__(self, gen):
            self._iter = iter(gen)
            self.count = 0
    
        def __next__(self):
            val = self._iter.__next__()
            self.count += 1
            return val
    
        def __iter__(self):
           return self
    
    c = GenCount(iglob(sys.argv[1]))
    for fn in c:
        print(fn)
    print(c.count)
    
    
    c = GenCount(iglob(sys.argv[1]))
    print([fn for fn in c])
    print(c.count)
    

    【讨论】:

      【解决方案7】:

      我发现这个解决方案好多了。查看此链接了解更多信息(http://python-notes.curiousefficiency.org/en/latest/python_concepts/break_else.html)。

      您可以使用自定义哨兵: x = no_data = object()

      x = no_data = object()
      for x in data:
          .......
      if x is no_data:
          print "Loop did not run"
      

      object() 返回一个无特征的对象,它是所有类的基础。

      检查两个对象是否相同(x 是 no_data)。如果它们保持相同,则表示控件从未进入 for 循环。

      【讨论】:

      • 我认为当 x = no_data 都指向一个生成器时,这可能会有一些风险,导致生成器在 for 循环后会耗尽。
      • 首先,xno_data 都指向一个 object 实例。 datax 从中获取值的生成器。如果循环运行,那么x 将发生变化,x is no_data 肯定会为 False。在我看来,没有风险。
      • @Menglong 你说的风险是什么?
      • 我认为这是for 循环最干净的解决方案。恕我直言,调用标记对象no_data_sentinel 会使代码更清晰。
      猜你喜欢
      • 2022-11-23
      • 2018-05-06
      • 2020-07-05
      • 1970-01-01
      • 2015-06-06
      • 2016-08-12
      • 1970-01-01
      • 2012-05-04
      • 2012-06-27
      相关资源
      最近更新 更多