不间断循环迭代的计数



在Python中,我经常发现自己实现了相同的模式:在循环中处理时计算"有效"迭代的次数,其中"无效"迭代被继续语句跳过。我使用 continue 语句而不是if-else块来提高可读性。本质上,我执行以下操作:

count = 0
for item in collection:
do_something_1(item)
if not evaluate_some_condition(item):
continue
count += 1
do_something_2(item)
return count

有几个漂亮的技巧可以用来以 Python 的方式实现类似的模式。例如,enumeratecontinuebreakfor-elsewhile-else浮现在脑海中。我正在寻找一个 Pythonic 结构来实现上述场景。

这有效(如下),但需要为每个元素执行两次evaluate_some_condition函数,这有时是不可接受的(在我看来,它的可读性也较低):

count = sum(1 for item in collection if not evaluate_some_condition(item))
for item in collection:
do_something_1(item)
if not evaluate_some_condition(item):
continue
do_something_2(item)
return count

像下面这样的结构将是理想的:

for count, item in uninterrupted_enumerate(collection):
do_something_1(item)
if not evaluate_some_condition(item):
continue
do_something_2(item)
return count

关于内置 Python 功能、第三方功能或未来包含此类功能的任何想法?

不,我认为第一个版本:

count = 0
for item in collection:
do_something_1(item)
if not evaluate_some_condition(item):
continue
count += 1
do_something_2(item)
return count

几乎就是这样完成的,并且非常明显(即可读)正在发生的事情。如果您将其与上一版本进行比较,则不清楚是否正在发生任何计数。如果你真的想以这种方式解决它,你总是可以在evaluate_some_condition函数中增加一些global变量,这取决于内部检查是否成功。但同样,这可能不如您的第一个版本可读。

我认为最有趣的选择是使用装饰器。因为这是在另一篇文章中描述的,所以我将简单地提供链接:https://stackoverflow.com/a/44969343/8033585

另一种选择是使用类。例如:

class Doer2:
def __init__(self):
self.count = 0
def __call__(self, item):
self.count += 1
# put here the code from 'do_something_2()'
....

然后:

doer2 = Doer2()
for item in collection:
do_something_1(item)
if not evaluate_some_condition(item):
continue
doer2(item)
return doer2.count

以下是使用装饰器的解决方案:

from typing import Callable

def do_something_1(item) -> None:
...

def do_something_2(item) -> None:
...

def evaluate_some_condition(item: int) -> bool:
return item % 2 == 0

def count_complete_iterations(
process_item: Callable[..., bool]
) -> Callable[..., int]:
count = 0
def wrapper(*args, **kwargs) -> int:
nonlocal count
if process_item(*args, **kwargs):
count += 1
return count
return wrapper

@count_complete_iterations
def process_int(item: int) -> bool:
do_something_1(item)
if not evaluate_some_condition(item):
return False
do_something_2(item)
return True

collection = range(10)
for item in collection:
count = process_int(item)
print(count)  # Prints 5

在这里,do_something_1do_something_2什么都不做; 如果item为偶数,则返回Trueevaluate_some_condition,如果为奇数,则返回False;count_complete_iterations是一个装饰器函数,它返回process_item返回True次数的最新计数;process_int是 for 循环 for 循环的内部部分,其中continue语句已替换为return False以指示无效项。

优点是它稍微简洁一些。缺点是它的透明度要低得多,循环中的逻辑需要放在它自己的函数中。我想说这可能是一个糟糕的解决方案,但考虑这一点很有趣。

最新更新