Skip to main content
 首页 » 编程设计

Python any(iterable) 和 all(iterable) 没有短路和副作用

2025年05月04日14tintown

我有以下代码:

def evAnd(v, *predicates): 
    satisfied=True 
    for f in predicates: 
        if not f(v): 
            satisfied=False 
            # log: f,v->False in a map and other side effects 
        else: 
            # log: f,v->True in a map and other side effects 
    return satisfied 
 
 
def evOr(v, *predicates): 
    satisfied=False 
    for f in predicates: 
        if f(v): 
            satisfied=True 
            # log: f,v->True in a map and other side effects 
        else: 
            # log: f,v->False in a map and other side effects 
    return satisfied 

将上述内容统一到一个函数中的 Pythonic 方法是什么? (因为放置日志消息的地方有相当多的副作用代码)请注意副作用的存在以及需要评估所有谓词的结果,而不会使 any 短路和 all

基于已接受答案的解决方案

所以,这是我最终根据接受的答案所做的:

def adorn(predicate): 
    def rv(v): 
        rvi = predicate(v) 
        if rvi: 
            print "%s is satisfied for value %d" % (predicate.__name__, v) 
            # any other side effects 
        else: 
            print "%s is not satisfied for value %d" % (predicate.__name__, v) 
            # any other side effects 
        return rvi 
    return rv 
 
 
def my_all(n, predicates): 
    return reduce(operator.and_, map( lambda x : x(n), map(adorn, predicates)), True) 
 
def my_any(n, predicates): 
    return reduce(operator.or_, map( lambda x : x(n) , map(adorn, predicates)), False) 

它可以通过以下方式进行测试:

def even(n): 
    return n%2==0 
 
def odd(n): 
    return n%2!=0 
 
print my_all(3, [even, odd]) 
print my_any(4, [even, odd]) 

请您参考如下方法:

一般来说,如果你将生成器表达式传递给 any 或 all 它会被短路,但如果你将它设为 LC 并将其传递给这两个构建-ins,短路是不可能的,你会得到你想要追求的效果

以下演示是不言自明的,可以根据您的问题进行调整

>>> count = 0 
>>> def foo(n): 
    global count 
    count += 1 
    return n%2 
 
>>> any(foo(n) for n in range(10)) 
True 
>>> count 
2 
>>> count = 0 
>>> any([foo(n) for n in range(10)]) 
True 
>>> count 
10 

根据 Blender 的建议,它可能会创建一个应该被丢弃的列表。更面向生成器的解决方案如下

研究此问题的另一种方法(至少对于生成器而言)是,如果被 any 或 all 短路,您希望消耗剩余的可迭代对象。你可以很容易地这样做,部分借用 consume itertools recipe

>>> count 
0 
>>> it = (foo(n) for n in range(10)) 
>>> any(it) 
True 
>>> collections.deque(it, maxlen = 0) 
deque([], maxlen=0) 
>>> count 
10 

这里有两个版本的 any 和 all 不会短路。随意给这些函数起一个有意义的名字(我真的很糟糕)

>>> def all_noss(expr): 
    it = iter(expr) 
    result = any(it) 
    collections.deque(it, maxlen = 0) 
    return result 
 
>>> def any_noss(expr): 
    it = iter(expr) 
    result = any(it) 
    collections.deque(it, maxlen = 0) 
    return result