1

我有以下代码:

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 方法是什么?(因为放置日志消息的地方有相当多的副作用代码)注意副作用的存在以及需要评估所有谓词的结果,而不会短路任何所有

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

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

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])
4

3 回答 3

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

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

any研究这个问题的另一种方法(至少对于生成器)是,如果被or短路,您希望消耗其余的可迭代对象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

这里有两个版本的 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
于 2013-02-01T08:26:34.583 回答
2

简单点怎么样

reduce(operator.and_, seq)

对于“所有”,和

reduce(operator.or_, seq)

对于“任何”。

于 2013-02-01T09:08:48.833 回答
1

您可以使用reduce

def evAnd(v, *predicates):
    return reduce(lambda x, y: x and y, [f(v) for f in predicates])

def evOr(v, *predicates):
    return reduce(lambda x, y: x or y, [f(v) for f in predicates])

或使用allor any

def evAnd(v, *predicates):
    return all([f(v) for f in predicates])

def evOr(v, *predicates):
    return any([f(v) for f in predicates])

由于上述方法创建了一个列表推导,所有谓词都将在没有短路的情况下进行评估。

更新

列表推导式的缺点是它们会在内存中创建列表,如果您担心,我们可以使用生成器:

def evAnd(v, *predicates):
    return reduce(lambda x, y: x and y, (f(v) for f in predicates))

def evOr(v, *predicates):
    return reduce(lambda x, y: x or y, (f(v) for f in predicates))

这次我们必须使用reduce, whileallany会有短路,这是不可取的。

@NPE 提醒,我们可以用运算符替换 lambda:

def evAnd(v, *predicates):
    return reduce(operator.and_,  (f(v) for f in predicates))

def evOr(v, *predicates):
    return reduce(operator.or_,  (f(v) for f in predicates))
于 2013-02-01T08:28:00.013 回答