3

如何检查我的循环是否从未运行过?

这对我来说看起来太复杂了:

x = _empty = object()
for x in data:
    ... # process x
if x is _empty:
    raise ValueError("Empty data iterable: {!r:100}".format(data))

没有更简单的解决方案吗?

上述解决方案来自好奇效率.org

更新

  • 数据可以包含None项目。
  • data 是一个迭代器,我不想使用它两次。
4

7 回答 7

3

“从未跑过”是指data没有元素吗?

如果是这样,最简单的解决方案是在运行循环之前检查它:

if not data:
    raise Exception('Empty iterable')

for x in data:
    ...

但是,正如下面评论中提到的,它不适用于某些可迭代对象,例如文件、生成器等,因此应谨慎应用。

于 2015-02-02T10:14:03.320 回答
2

最好是原始代码。

x = _empty = object()

_empty称为哨兵值。在 Python 中,用 来创建哨兵是很常见的object(),因为很明显 的唯一目的_empty是成为一个虚拟值。但是您可以使用任何可变的,例如空列表[]

当您将可变对象与 进行比较时,始终保证它们是唯一的is,因此您可以安全地将它们用作标记值,这与诸如Noneor之类的不可变对象不同0

>>> None is None
True
>>> object() is object()
False
>>> [] is []
False
于 2015-02-02T11:06:05.587 回答
1

以下简单的解决方案适用于任何可迭代的。它基于这样的想法,即我们可以检查是否存在(第一个)元素,如果存在则继续迭代。结果更清晰:

import itertools

try:
    first_elmt = next(data)
except StopIteration:
    raise ValueError("Empty data iterator: {!r:100}".format(data))

for x in itertools.chain([first_elmt], data):
    …

PS:请注意,它假定它data是一个迭代器(如问题所示)。如果它只是一个可迭代的,则代码应该在data_iter = iter(data)而不是 on 上运行data(否则,如果data是一个列表,循环将复制第一个元素)。

于 2015-02-02T10:53:44.253 回答
0

这个解决方案怎么样?

data=[]

count=None
for count, item in enumerate(data):
    print (item)

if count is None:
    raise ValueError('data is empty')
于 2015-02-02T12:18:53.797 回答
0

您可以将loop_flag默认值添加为 False,当循环执行时,将其更改为 True:

loop_flag = False
x = _empty = object()

for x in data:
    loop_flag = True
    ... # process x

if loop_flag:
    print "loop executed..."
于 2015-02-02T10:08:05.133 回答
0

我提出以下建议:

loop_has_run = False
for x in data:
    loop_has_run = True
    ... # process x
if not loop_has_run:
    raise ValueError("Empty data iterable: {!r:100}".format(data))

我认为这比问题中的示例更好,因为:

  • 意图更清晰(因为变量名直接指定了它的含义)。
  • 不会创建或销毁任何对象(这可能会对性能产生负面影响)。
  • 它不需要注意object()总是返回唯一值的细微之处。

请注意,loop_has_run = True分配应放在循环的开头,以防(例如)循环体包含break.

于 2015-02-02T11:23:49.943 回答
0

该代码的意图并不是很明显。当然人们会在一段时间后理解它,但代码可以更清晰。

我提供的解决方案需要更多的代码行,但该代码位于可以存储在其他地方的类中。此外,此解决方案适用于可迭代对象和迭代器以及大小容器。

您的代码将更改为:

it = HadItemsIterable(data)
for x in it:
    ...
if it.had_items:
    ...

该类的代码如下:

from collections.abc import Iterable
class HadItemsIterable(Iterable):

    def __init__(self, iterable):
        self._iterator = iter(iterable)

    @property
    def had_items(self):
        try:
            return self._had_items
        except AttributeError as e:
            raise ValueError("Not iterated over items yet")

    def __iter__(self):
        try:
            first = next(self._iterator)
        except StopIteration:
            if hasattr(self, "_had_items"):
                raise
            self._had_items = False
            raise
        self._had_items = True
        yield first
        yield from self._iterator
于 2015-02-02T10:32:26.107 回答