我刚刚写了一些我想做的代码:
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个对象),尽管一般情况更有用。我也尝试优化以便于阅读,而不是为了表现。
有没有比这更好的构造?
答案 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;