在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 的方式实现类似的模式。例如,enumerate
、continue
、break
、for-else
和while-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_1
和do_something_2
什么都不做; 如果item
为偶数,则返回True
evaluate_some_condition
,如果为奇数,则返回False
;count_complete_iterations
是一个装饰器函数,它返回process_item
返回True
次数的最新计数;process_int
是 for 循环 for 循环的内部部分,其中continue
语句已替换为return False
以指示无效项。
优点是它稍微简洁一些。缺点是它的透明度要低得多,循环中的逻辑需要放在它自己的函数中。我想说这可能是一个糟糕的解决方案,但考虑这一点很有趣。