如何检查我的循环是否从未运行过?
这对我来说看起来太复杂了:
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 是一个迭代器,我不想使用它两次。
如何检查我的循环是否从未运行过?
这对我来说看起来太复杂了:
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没有元素吗?
如果是这样,最简单的解决方案是在运行循环之前检查它:
if not data:
raise Exception('Empty iterable')
for x in data:
...
但是,正如下面评论中提到的,它不适用于某些可迭代对象,例如文件、生成器等,因此应谨慎应用。
最好是原始代码。
x = _empty = object()
_empty称为哨兵值。在 Python 中,用 来创建哨兵是很常见的object(),因为很明显 的唯一目的_empty是成为一个虚拟值。但是您可以使用任何可变的,例如空列表[]。
当您将可变对象与 进行比较时,始终保证它们是唯一的is,因此您可以安全地将它们用作标记值,这与诸如Noneor之类的不可变对象不同0。
>>> None is None
True
>>> object() is object()
False
>>> [] is []
False
以下简单的解决方案适用于任何可迭代的。它基于这样的想法,即我们可以检查是否存在(第一个)元素,如果存在则继续迭代。结果更清晰:
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是一个列表,循环将复制第一个元素)。
这个解决方案怎么样?
data=[]
count=None
for count, item in enumerate(data):
print (item)
if count is None:
raise ValueError('data is empty')
您可以将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..."
我提出以下建议:
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.
该代码的意图并不是很明显。当然人们会在一段时间后理解它,但代码可以更清晰。
我提供的解决方案需要更多的代码行,但该代码位于可以存储在其他地方的类中。此外,此解决方案适用于可迭代对象和迭代器以及大小容器。
您的代码将更改为:
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