替换返回最后一个评估对象的`any`?

时间:2015-05-21 03:47:40

标签: python

我刚刚写了一些我想做的代码:

def foo(container)
    return any((some_obj.attr <= 0 for some_obj in container))

其中foo会返回some_obj,其中some_obj.attr为零或更少。我想,另一种选择是

def foo(container):
    return next((some_obj for some_obj in container if some_obj.attr <= 0), False)

但这感觉非常黑客。

我最终把它写出来了,但我不喜欢它有多深的嵌套。

def foo(container):
    for some_obj in container:
        if some_obj.attr <= 0:
            return some_obj
    return False

澄清:container在这种情况下可能不超过6个对象(通常是2个对象),尽管一般情况更有用。我也尝试优化以便于阅读,而不是为了表现。

有没有比这更好的构造?

3 个答案:

答案 0 :(得分:12)

any的文档说明它等同于:

def any(iterable):
    for element in iterable:
        if element:
            return True
    return False

因此,如果代码与用于说明any本身功能的代码具有完全相同的结构,我认为您的代码不会太嵌套。

不过,我可能会把它包装成一个函数:

def first(iterable, predicate):
    for element in iterable:
        if predicate(element):
            return element
    return False

所以现在你可以这样做:

def foo(container):
    return first(container, lambda obj: obj.attr <= 0)

或者,或者,只需使用生成器表达式,并将其传递给双参数next,就像您已经在做的那样:

def foo(container):
    return next((obj for obj in container if obj.attr <= 0), False)

具有相同的“深度”,它只是水平而不是垂直。

或者,也许,拔出genexpr并命名它:

def foo(container):
    nonpositives = (obj for obj in container if obj.attr <= 0)
    return next(nonpositives, False)

你会如何在他们之间做出选择?我认为如果谓词太复杂而无法读作lambda但又不够复杂,不值得抽象成一个外联函数,我会选择genexpr。否则,包装函数。但这真的是一种品味问题。

答案 1 :(得分:5)

next(filter应该这样做,这是测试<= 0的有趣方式:

>>> next(filter((0).__ge__, [3,2,1,-1,-2]), False)
-1
哈,甚至是招摇道:

>>> next(filter(0..__ge__, [3,2,1,-1,-2]), False)
-1

或者,正如abarnert指出的那样:

>>> next(filter(0 .__ge__, [3,2,1,-1,-2]), False)
-1

答案 2 :(得分:3)

只是为了好玩,扩展Stefan Pochmann's answer来处理obj.attr <= 0,仍然不需要lambda:

from operator import attrgetter
from functional import compose

next(filter(compose(0..__ge__, attrgetter('attr')), [3, 2, 1, -1, -2]), False)

如果您没有functional模块(您可能不会这样做,因为PyPI上的版本自Python 2.4以来就没有工作......)并且没有&#39 ; t想要搜索现代替代品,你可以自己写compose(稍好一点):

def compose(f, g):
    @functools.wraps(f):
    def wrapper(x):
        return f(g(x))
    return wrapper

大约每年一次,有一个建议将compose添加到stdlib,甚至可能给它一个中缀运算符。使用@ being added for matrix multiplication,您可以猜出最新的提案。 * 因此,如果发生这种情况(它可能赢了),您可以这样做:

from operator import attrgetter

next(filter(0..__ge__ @ attrgetter('attr'), [3, 2, 1, -1, -2]), False)

现在我们唯一需要的是Haskell风格的operator sectioning所以我们可以摆脱绑定方法,.. hack以及attrgetter函数的需要(假设你将点归属视为运算符,它实际上并非如此,但让我们假装......)。然后:

next(filter((<= 0) @ (.attr), [3, 2, 1, -1, -2]), False)

*事实上,在最初的PEP 465讨论期间提出了两次,这就是PEP提到的原因,&#34;在讨论这个PEP时,提出了类似的建议来定义{{1作为通用函数组合运算符,这也遇到了同样的问题; @甚至不足以存在。&#34;