Python中带有回调函数的any()函数

91

Python标准库定义了一个any()函数,它能够检查可迭代对象中是否存在元素为真。如果可迭代对象为空,则返回False。

any()函数只检查元素是否为True。我想指定一个回调函数来判断元素是否符合条件,例如:

any([1, 2, 'joe'], lambda e: isinstance(e, int) and e > 0)

5
如果您愿意,您总是可以使用 any(map(lambda:..., [...])),但是使用生成器表达式更符合惯用语。 - Thomas Ahle
8个回答

155
怎么样:
>>> any(isinstance(e, int) and e > 0 for e in [1,2,'joe'])
True

当然也可以使用all()

>>> all(isinstance(e, int) and e > 0 for e in [1,2,'joe'])
False

10
这种类型函数的问题不就是它首先会创建一个布尔列表,然后检查其中是否有一个为真吗?而不是在找到第一个有效实例后停止吗? - Joel Harkes
10
只要您不将可迭代对象用方括号 []list() 包装起来,它就会使用生成器并按预期工作。 - Ian Hunter

21

any函数在任意条件为真时返回True。

>>> any(isinstance(e, int) and e > 0 for e in [0 ,0, 1])
True # Returns True because 1 is greater than 0.


>>> any(isinstance(e, int) and e > 0 for e in [0 ,0, 0])
False # Returns False because not a single condition is True.

实际上,“any”函数的概念源自Lisp,或者说源自函数式编程方法。还有另一个与之相反的函数,名为all

>>> all(isinstance(e, int) and e > 0 for e in [1, 33, 22])
True # Returns True when all the condition satisfies.

>>> all(isinstance(e, int) and e > 0 for e in [1, 0, 1])
False # Returns False when a single condition fails.

当正确使用时,这两个函数非常棒。


14
你应该使用“生成器表达式”——一种语言结构,可以在单行上消耗迭代器并对其进行过滤和表达式运算。例如,(i ** 2 for i in xrange(10))是前10个自然数(0到9)的平方的生成器。它们还允许在“for”子句上使用“if”子句来过滤项目,因此在你的例子中,可以使用:
any (e for e in [1, 2, 'joe'] if isinstance(e, int) and e > 0)

1
感谢您提到生成器,因为我认为这是使它最像Lambda版本的原因(在不必处理整个列表的情况下,如果早期项目为false)。另外,很高兴知道如果它是唯一的参数,我们可以省略生成器的括号。不知何故错过了这一点... - ShawnFumo

9
Antoine P的回答稍有改进
>>> any(type(e) is int for e in [1,2,'joe'])
True

all()函数

>>> all(type(e) is int for e in [1,2,'joe'])
False

6
尽管其他人都给出了很好的Python风格的回答(在大多数情况下,我会使用被接受的答案),但我只想指出如果你真的喜欢的话,制作自己的实用函数来完成这个任务是多么容易:

def any_lambda(iterable, function):
  return any(function(i) for i in iterable)

In [1]: any_lambda([1, 2, 'joe'], lambda e: isinstance(e, int) and e > 0
Out[1]: True
In [2]: any_lambda([-1, '2', 'joe'], lambda e: isinstance(e, int) and e > 0)
Out[2]: False

我认为至少应该先用函数参数定义它,因为这更接近现有的内置函数,如map()和filter():

def any_lambda(function, iterable):
  return any(function(i) for i in iterable)

5
你可以使用anymap的组合,如果你真的想保留lambda符号,代码如下:
any(map(lambda e: isinstance(e, int) and e > 0, [1, 2, 'joe']))

但最好使用生成器表达式,因为它不会两次构建整个列表。


4

筛选器可以起作用,而且它会返回匹配的元素

>>> filter(lambda e: isinstance(e, int) and e > 0, [1,2,'joe'])
[1, 2]

2
虽然这是可行的(因为空列表为False),但与使用any相比,它有几个缺点。首先,它将遍历整个列表(即使第一个元素为False),并将True项目复制到新列表中,增加时间和内存。最后,如果你只是检查任何项是否匹配某些内容,它会使你的意图不太清晰。如果你需要立即使用匹配的项,我同意它可能很有用(根据你如何使用结果考虑itertools.ifilter)。 - ShawnFumo

3
如果你真的想在 any() 中内联一个 lambda,你可以这样做:
>>> any((lambda: isinstance(e, int))() for e in [1,2,'joe'])
True
>>> any((lambda: isinstance(e, int))() for e in ['joe'])
False

你只需要包装未命名的lambda,并确保在每次传递时调用它,通过追加()来实现。

这里的优点是,在遇到第一个int时仍然可以利用短路来评估任何when的值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接