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

Mar*_*tus 1 python

我有以下代码:

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
Run Code Online (Sandbox Code Playgroud)

什么是Pythonic在单一功能中统一上述内容的方法?(因为存在相当大的副作用代码,其中放置了日志消息)注意副作用的存在以及评估所有谓词的结果而不需要短路任何所有谓词的需要

基于公认答案的解决方案

所以,这就是我最后根据接受的答案所做的事情:

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)
Run Code Online (Sandbox Code Playgroud)

它可以测试:

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])
Run Code Online (Sandbox Code Playgroud)

Abh*_*jit 5

一般来说,如果你传递一个生成器表达式any或者all它会被短路但是如果你把它作为一个LC并将它传递给这两个内置函数,那么短路是不可能的,你会得到你想要的效果追求

以下演示是自解释的,可以适应您的问题

>>> 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
Run Code Online (Sandbox Code Playgroud)

正如Blender所建议的那样,它可能会创建一个可以丢弃的列表.更面向发电机的解决方案如下

另一种研究这个问题的方法(至少对于生成器而言)是,如果被anyor 短路,你想要消耗其余的iterable all.通过部分借用消耗的itertools配方,您可以轻松完成此操作

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

这里有两个版本的任何和所有不会短路的版本.随意给这些函数一个有意义的名字(我真的很糟糕)

>>> 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
Run Code Online (Sandbox Code Playgroud)