Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何检查我的循环是否从未运行过?_Python_Design Patterns_Code Cleanup_Empty List - Fatal编程技术网

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
,会隐式执行分配,但我尚未确认这一点。同意,与问题中引用的解决方案相比,是否存在性能损失取决于数据的大小。现在,这并没有改变这样一个事实,即在循环中一遍又一遍地进行赋值是浪费的,而且在算法上也是可疑的,但我不认为这太糟糕。:)我不确定在我的解决方案中,您可以指的是什么“分配”:可能是创建一个列表?