Python 仅在 for 循环未开始迭代(使用生成器)时才执行代码?

问题描述 投票:0回答:9

如果迭代完成但不被

else
打断,则
for
/
else
子句中的
break
块将被执行,所以我读到了

是否有一种语言构造可以让我编写一些仅在

for
循环没有开始迭代时才执行的东西?如果我使用
tuple
list
,我会做这样的事情:

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

我怎样才能做到这一点而不用尽全力从生成器创建

tuple
list
,同时还使用
for
循环?我可以在
next()
循环中使用
while
并尝试捕获
StopIteration
,但我想看看是否有一个很好的方法来使用
for
来做到这一点。

python python-2.7 generator
9个回答
8
投票

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

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

6
投票
n = -1
for n, i in enumerate(it):
    do_stuff()
if n < 0:
    print 'Done'

5
投票

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

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

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

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

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


2
投票

您可以使用生成器函数:

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

0
投票

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

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

我的语法可能是错误的,因为我不是Python专家..


0
投票

您可以编写一个包装器来计算迭代次数。它的优点是可以处理更奇特的枚举。在 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)

0
投票

可以检查循环中是否定义了x。

for x in (y for y in range(2) if y > 1):
    print(x)

try:
    print(f'do something with {x}')
except NameError:
    print('the loop did not run')

但是,请确保 x 未在循环之前定义。


0
投票

else
分支是有副作用的表达式(或表达式序列)时,可以使用布尔运算符的短路属性。例如,这个 Python 3 代码:

def print_items_or_nothing(items):
    if items == []:
        print("nothing")
    else:
        for item in items:
            print(item)

print_items_or_nothing([])          # prints "nothing"
print_items_or_nothing([1, 2, 3])   # prints "1", "2", "3"

...可以重写为:

def print_items_or_nothing(items):
    for item in items or print("nothing") or []:
        print(item)

说明

该技术利用了以下事实:当且仅当左侧操作数为假时,

or
才会计算并返回其右侧操作数。

考虑表达式

items or print("nothing") or []
:

  • 如果
    items
    非空,则为真,因此不计算表达式的其余部分,结果为
    items
  • 如果
    items
    为空,则为假,因此
    print("nothing")
    被求值...为
    None
    ,这是假的,因此
    []
    被求值并构成结果,该结果是可迭代的(根据需要)。

优点

  • 无需修改循环体;
  • 无辅助变量;
  • 轻量级。

缺点

  • 仅限于
    else
    分支不包含指令的情况(尽管您当然可以使用海象运算符来增加不可读性);
  • 聪明;)

-1
投票

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

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

*编辑OP评论*t

© www.soinside.com 2019 - 2024. All rights reserved.