Python 如何检查我的循环是否从未运行过?
如何检查我的循环是否从未运行过 这在我看来似乎太复杂了:Python 如何检查我的循环是否从未运行过?,python,design-patterns,code-cleanup,empty-list,Python,Design Patterns,Code Cleanup,Empty List,如何检查我的循环是否从未运行过 这在我看来似乎太复杂了: x = _empty = object() for x in data: ... # process x if x is _empty: raise ValueError("Empty data iterable: {!r:100}".format(data)) 难道没有更简单的解决办法吗 data=[] count=None for count, item in enumerate(data): print
x = _empty = object()
for x in data:
... # process x
if x is _empty:
raise ValueError("Empty data iterable: {!r:100}".format(data))
难道没有更简单的解决办法吗
data=[]
count=None
for count, item in enumerate(data):
print (item)
if count is None:
raise ValueError('data is empty')
上述解决方案来自
更新
- 数据可以包含
项None
- 数据是一个迭代器,我不想使用它两次
循环\u标志
默认为False,执行循环时,将其更改为True:
loop_flag = False
x = _empty = object()
for x in data:
loop_flag = True
... # process x
if loop_flag:
print "loop executed..."
“从不运行”是指数据没有元素吗
如果是这样,最简单的解决方案是在运行循环之前检查它:
if not data:
raise Exception('Empty iterable')
for x in data:
...
但是,正如下面的评论中所提到的,它不能用于某些iterables,如文件、生成器等,因此应小心应用。该代码的意图并不明显。当然,人们会理解它一段时间后,但代码可以更清楚
我提供的解决方案需要更多的代码行,但这些代码位于可以存储在其他地方的类中。此外,此解决方案还适用于iterables和迭代器以及大小合适的容器
您的代码将更改为:
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
以下简单的解决方案适用于任何iterable。它基于这样一种想法,即我们可以检查是否存在(第一个)元素,然后继续迭代是否存在。结果更加清楚:
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):
…
注意,它假设数据是一个迭代器(如问题中所述)。如果它只是一个iterable,那么代码应该在data\u iter=iter(data)
上运行,而不是在data
上运行(否则,如果data
是一个列表,那么循环将复制第一个元素)。原始代码是最好的
x = _empty = object()
\u empty
被称为a。在Python中,使用object()
创建sentinel是很常见的,因为很明显\u empty
的唯一目的就是作为一个伪值。但是您可以使用任何可变项,例如空列表[]
当您将可变对象与is
进行比较时,始终保证它们是唯一的,因此您可以安全地将它们用作前哨值,而不像无
或0
等不可变对象
>>> None is None
True
>>> object() is object()
False
>>> [] is []
False
我提议如下:
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\u has\u run=True
赋值应该放在循环的开头,如果(例如)循环体包含break,那么这个解决方案呢
data=[]
count=None
for count, item in enumerate(data):
print (item)
if count is None:
raise ValueError('data is empty')
data
是列表还是其他类似的容器?如果data
是列表,为什么不使用如果不是数据:
?为什么您认为这太复杂了?它简单易懂。@HåkenLid许多原因使引用的代码变得复杂:(1)你必须阅读并记住第一行,而不理解它的目的。(2) 循环通常会改变x
,这是不寻常的,因为我们刚刚设置了x
:发生了什么?(3) 如果data
的最后一个元素是object()
,则末尾的测试不起作用:这是有意的吗?这会发生吗?我们真的在测试数据的空性吗?(4) 该测试旨在说明“数据是否为空”实际上读取的是“数据是否为空”。它之所以复杂的另一个原因是有一个更简单的解决方案(见我的答案)正如@HåkenLid所指出的,(3)实际上不是问题,因为object()
创建了一个新对象(它不是一个单例)。也没有那么大的帮助。为什么对x
和循环标记都进行测试?这是不必要的冗余。loop_flag=True应该是第一条语句。否则,“进程x”中的continue
将产生意外结果。如果数据
是一个序列,这将起作用-如果它是一个iterable
它将始终是真的,即使它永远不会产生任何可以处理某些iterable的元素,但不能处理所有iterable(例如文件或生成器函数/表达式).我认为检查sentinel值是可以的;不确定OP有什么问题。因为更新后的问题提到它正在使用迭代器,所以此解决方案不起作用。我真的不同意原始代码是最好的:我对问题的评论解释了原因(本质上:代码很复杂,有一个更清晰的解决方案)。object()
每次都创建一个唯一的对象,这样就不会有问题了。你说得对,我的错。我删除了相关的注释。这还不错,但这有一个不幸的副作用,即在每个迭代中强制执行一个无用的赋值,而不是第一个(这使得您关于创建对象的惩罚的第二点没有实际意义)。这样的惩罚是没有必要的(见我的答案,这是我在现实生活中会用到的:)@EOL:一般来说,任务的惩罚要比创建/销毁对象的惩罚便宜得多。显然,这对实际运行时的影响会有所不同,特别是取决于循环的典型迭代次数(始终测量!)。我相信您的解决方案由于调用itertools.chain
,会隐式执行分配,但我尚未确认这一点。同意,与问题中引用的解决方案相比,是否存在性能损失取决于数据的大小。现在,这并没有改变这样一个事实,即在循环中一遍又一遍地进行赋值是浪费的,而且在算法上也是可疑的,但我不认为这太糟糕。:)我不确定在我的解决方案中,您可以指的是什么“分配”:可能是创建一个列表?