【问题标题】:Loop over empty iterator does not raise exception循环空迭代器不会引发异常
【发布时间】:2020-05-20 05:04:29
【问题描述】:

我使用的是 Python 3.6.7。

我刚刚注意到一个空列表上的for 循环甚至不会循环一次。 经过一番思考,这对我来说有点道理。 IE。对零大小(空)对象的循环返回零迭代。

iterable = []
for element in iterable:
    pass
print(element)
>>> NameError: name 'element' is not defined

这意味着如果len(iterable) == 0,则不会执行循环内的测试。

iterable = []
for element in iterable:
    assert isinstance(element, int)
#nothing happens

那我怎样才能捕捉到这种情况呢?

当我的循环因为可迭代对象为空而无法运行时,是否有一种紧凑的内置方法来引发错误?

捕捉这种特殊情况需要手动:

  • 在循环之前测试iterator 是否为非空
  • 在循环之后测试元素是否已定义。两者都不 方法对我来说似乎很优雅

我最终可能会在每个 for 循环中进行此测试。

assert len(iterable) > 0
#loop

#loop
assert "element" in dir() #?

【问题讨论】:

  • 你可以只使用assert len(iterator) > 0 而不是assert iterator - 空迭代器是错误的:bool([]) == False
  • 请注意,默认情况下迭代器既没有长度也没有布尔意义。 Iterables 可以,但它们只是充当迭代器的一小部分。
  • 您的问题到底是什么?你想捕获空迭代器,你想断言一个空迭代器的嵌套类型,你想要一个空迭代器有用的例子,你想在空循环上引发异常,...?
  • MisterMiyagi 我已经不再考虑使用空迭代器了。这使得问的问题更清楚(Is there a compact built-in way to raise an error when my loop does not run because the iterable is empty?
  • MisterMyagi 我编辑并替换了iteratoriterable

标签: python python-3.x for-loop


【解决方案1】:

您可以使用Statements, and else Clauses on Loops

循环语句可能有一个else 子句;它在循环通过耗尽迭代终止时执行。

例如:

iterator = []
for element in iterator:
    print('This wont print..')
else:
    assert iterator

这将导致:

Traceback (most recent call last):
  File "<pyshell#2>", line 4, in <module>
    assert iterator
AssertionError

【讨论】:

  • else 在这里没有意义。仅当您实际 break 循环时才有意义。在这里,就像在循环之后直接写assert一样
  • else 仅在loop exhausts 时生效,break 将阻止loop 停止其迭代,因此else 将不会发生。
  • 正是我的观点。这里没有break -> 所以循环总是 耗尽 -> 所以else 没有效果...
  • 我不接受这个答案,因为 Tomerikoo 的评论是正确的:无论iterable 是否为空,最后的else 都会执行
【解决方案2】:

如果由于列表为空而导致循环无法运行,您为什么需要做一些事情?这是完全正常的行为。如果您出于某种原因期望至少有一个项目(也许数组中没有项目是程序中错误的衡量标准),然后检查 len(iterator) == 0 是否如您之前建议的那样。这是一种完全有效的方法。

顺便说一句,当您检查变量的值等时,exeptions 是一种方法。断言更多是用于严重故障的硬编码信息,不应该用于报告常见错误,因为它们不能处理。

【讨论】:

  • 我的问题的上下文:我通常会捕获与模式匹配的文件夹中可用的文件列表。例如,[f for f in os.listdir() if f.endswith('.tar')],然后遍历这些。如果我没有文件,无论循环内引发异常,循环都不会执行,我会错过真正发生的事情。
  • @AndreaMassetti 我认为在迭代之前检查是可行的方法,因为如果循环没有运行,那么你就无法检查其中的任何内容。如果使用 oneliners 检查您的变量或列表太麻烦和/或需要多个语句,那么我会考虑将逻辑隔离到一个函数中。据我所知,这是最严重的方式。祝你好运。
【解决方案3】:

没有直接检查空迭代器的内置方法。空迭代器通常不被视为异常。但是,您可以定义紧凑的帮助器来显式检查空迭代器。


预先检测空迭代器通常是不可能的。在某些情况下,可以从迭代器的源中猜测它是否为空——例如,如果序列和映射的迭代器的父级为布尔值 false,则它们是空的。但是,迭代器本身并没有任何内容指示,并且实际上可能是“空的”,除非迭代结束。

唯一可靠的方法是检查迭代器在迭代时是否提供项目。 可以定义一个包装迭代器,如果迭代器根本不提供任何项,则会引发错误。

def non_empty(iterable):
    """Helper to ensure that ``iterable`` is not empty during iteration"""
    iterator = iter(iterable)
    try:
        yield next(iterator)  # explicitly check first item
    except StopIteration:
        raise LookupError(f'{iterable} is empty') from None
    yield from iterator       # forward iteration of later items

这样的帮助器可以包裹在可迭代对象和迭代器周围,并且可以在for 循环、显式iter 迭代器和任何其他迭代场景中工作。

>>> iterable = []
>>> for element in non_empty(iterable):
...    assert isinstance(element, int)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in non_empty
LookupError: [] is empty

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-07
    • 1970-01-01
    • 1970-01-01
    • 2011-07-01
    • 2018-12-30
    • 1970-01-01
    • 2021-08-18
    • 1970-01-01
    相关资源
    最近更新 更多