Python中的 any() 函数和 all() 函数
前言any()和all()是两个「看起来简单、用错了很难发现」的内置函数。它们的表面语义一句话能说完any()问「有没有一个为真」all()问「是不是全都为真」。但真正让它们有价值的是两个细节——短路求值short-circuit evaluation和空序列的返回值。后者尤其反直觉any([])是False而all([])却是True。先纠正一个常见误解any()和all()不是or/and的函数版本。运算符or/and返回的是操作数本身1 or 2得到1而any()/all()返回的是布尔值True或False。写惯x or default的人转过来用any()很容易以为它也会把某个元素「返回」出来。官方签名很短注意那个/表示参数只能按位置传any(iterable, /)all(iterable, /)本文以 Python 3 为基准写。这两个函数在 Python 2.5 就引入了所以不涉及什么版本差异但 Python 2.7 已于2020 年 1 月 1 日停止维护文中一律用 Python 3 的写法比如print(...)是函数调用形式。一、语义文档给的等价实现官方文档为了消除歧义直接给出了两者的等价代码# 适用于 Python 3.8这是文档给出的等价实现不是内置定义def all(iterable):for element in iterable:if not element:return Falsereturn Truedef any(iterable):for element in iterable:if element:return Truereturn False这段代码把两件事讲得很清楚判断的是元素的真值truthiness不是元素本身是不是True。一到结论就return不会把剩下的元素看完——这就是短路。官方对all的措辞是「如果可迭代对象的所有元素都为真返回True如果可迭代对象为空也返回True」。对any的措辞是「如果可迭代对象中有任意元素为真返回True如果可迭代对象为空返回False」。# 适用于 Python 3.8print(any([0, 0, 1])) # True —— 有一个为真就够了print(any([0, 0, 0])) # Falseprint(all([1, 2, 3])) # Trueprint(all([1, 0, 3])) # False —— 有一个为假就整体为假# 值不是布尔也没关系看的是真值print(any([, hi])) # Trueprint(all([[], [1], [2]])) # False —— 空列表 [] 是假值# 返回的一定是 boolprint(type(any([1]))) # class boolprint(1 or 2, 0 or 2) # 1 2 —— 对比运算符返回的是操作数最后两行是关键词any([1])给你True不是1。想拿到具体元素得用next((x for x in it if x), default)这类写法。二、短路求值为什么它比手写循环好短路的意义有两层。第一层是效率找到结论就停不用遍历完。第二层更重要——如果元素的计算本身有副作用或者代价高短路能避免多余的工作。# 适用于 Python 3.8def check(n):print(检查, n)return n 2print(any(check(n) for n in [1, 2, 3, 4]))在生成器表达式里any()依次取到1、2、3到3时判定为真就停止4根本不会被取到。所以「检查 4」不会打印。这就是短路在惰性生成器上的效果。这一点也解释了为什么用生成器表达式比用列表推导更合适写法是否惰性短路能否省掉计算any(x 3 for x in data)是生成器能any([x 3 for x in data])否先建完整列表不能列表已经全算完了any(map(pred, data))是map是惰性迭代器能any([...])这种写法在功能上没错但它会先把整个列表算出来再交给any遍历。数据量大或者判断代价高时白白做了一堆无用功。习惯上应该写成生成器表达式——省掉方括号而已。# 适用于 Python 3.8numbers [1, 2, 3, 400]# 惰性找到第一个 100 的就停print(any(n 100 for n in numbers)) # True# 也是惰性map 返回迭代器print(all(n 1000 for n in numbers)) # True# 提前退出的常见场景检查一批条件里有没有失败的checks [lambda: True, lambda: False, lambda: True]print(all(c() for c in checks)) # False第三个 lambda 不会被调用三、空序列为什么 any([]) 假、all([]) 真这是这两个函数最需要记牢的地方也最容易在真实代码里酿成意外。# 适用于 Python 3.8print(any([])) # Falseprint(all([])) # True两个都「反直觉」但道理并不难讲all问的是「有没有反例」。空集合里找不到任何反例所以答案是「全部满足」即True。这在数理逻辑里叫「空真vacuously true」。any问的是「有没有正例」。空集合里一个正例都没有所以答案是False。理解这一点之后就能看出为什么下面这类代码会出问题# 适用于 Python 3.8def all_positive(numbers):return all(n 0 for n in numbers)print(all_positive([1, 2, 3])) # Trueprint(all_positive([])) # True —— 空列表也算「全部为正」如果业务上「空列表」应当被当成失败或无效输入就必须显式判空不能指望all()替你拦下来# 适用于 Python 3.8def all_positive(numbers):if not numbers:return False # 空输入按业务规则处理return all(n 0 for n in numbers)反过来any([])为False的语义通常更符合直觉「没有找到」所以any()在空集合上的坑少一些。四、真值规则哪些值算「真」既然any/all判断的是真值那就必须知道 Python 的真值规则。默认情况下对象都被视为真以下这些是假值为假的值说明False布尔假None空值0、0.0、0j、Decimal(0)等数值零、b空字符串 / 空字节串[]、()、{}、set()、frozenset()、range(0)空容器判断顺序是先看类有没有__bool__有就用它的返回值没有就看有没有__len__用「长度是否为 0」都没有就视为真。自己写的类也遵循这套规则# 适用于 Python 3.8class Cart:def __init__(self, items):self.items list(items)def __len__(self):return len(self.items)print(all([Cart([1]), Cart([])])) # False —— 空购物车长度为 0是假值print(any([, None, [], 0.0])) # False —— 全是假值一个常见的意外来源是数值零all(nums)里只要有一个0结果就是False。如果你的本意是「全都存在」不是None那应当写成all(n is not None for n in nums)而不是all(nums)。五、把条件写在生成器里而不是列表里综合起来这两个函数最典型的用法是「把判断写成生成器表达式」。下面几组写法覆盖了大多数场景# 适用于 Python 3.8text The quick brown fox# 某条件是否至少有一个元素满足print(any(ch.isdigit() for ch in text)) # False# 是否全都满足print(all(ch.isascii() for ch in text)) # True# 用德摩根律等价改写words [hi, hey, hello]print(not any(w.startswith(x) for w in words)) # Trueprint(all(not w.startswith(x) for w in words)) # True —— 与上一行等价# 判断容器里有没有「非空」的元素rows [[], [], [1, 2]]print(any(rows)) # True# 判断是否所有元素都非空print(all(rows)) # False德摩根律这一条很实用not any(条件)和all(不是条件)等价。写代码时选哪个取决于哪个读起来更像自然语言。「没有任何一个以 x 开头」通常比「所有都满足不以 x 开头」更顺口。六、组合多个条件需要判断「同时满足多个条件」或「满足其中任意一个」时不要把它们堆在一个all()里让语义变模糊分清楚层次# 适用于 Python 3.8user {name: alice, age: 30, email: }# 全部字段都非空required (name, age, email)print(all(user.get(k) for k in required)) # Falseemail 是空串# 至少有一个联系方式contacts (email, phone)print(any(user.get(k) for k in contacts)) # False# 条件本身就是表达式时写成函数更易读def is_valid(u):has_name bool(u.get(name))has_age isinstance(u.get(age), int) and u[age] 0return all([has_name, has_age])print(is_valid(user)) # True最后那个all([...])用列表是可以的因为这些条件已经算出来了没有短路可省但如果某一个条件的计算很贵写成生成器会更省。常见坑点1. 以为all([])是False。❌if all(score 60 for score in scores):直接当「有成绩且都及格」用 —— 空列表会走进「及格」分支。 ✅ 先判空if scores and all(...):或按业务规则显式处理空输入。2. 把any()当成「返回第一个为真的元素」。❌value any([0, , hi])期望拿到hi—— 实际得到True。 ✅ 要拿元素用next((x for x in it if x), default)any()只回答「有没有」。3. 对str.find()的结果用any()。❌any(s.find(word) for word in words)——find找不到时返回-1而-1是真值结果恒为True。 ✅ 显式比较any(s.find(word) ! -1 for word in words)或更 Python 的any(word in s for word in words)。4. 用列表推导配合 any/all丢掉了短路。❌any([expensive(x) for x in data])—— 列表先被完整算出来短路没意义了。 ✅any(expensive(x) for x in data)生成器表达式逐个求值找到答案就停。5. 传进去的是生成器之后还想再用一次。❌ok all(x 0 for x in gen)之后再list(gen)—— 生成器已被消费第二次是空的。 ✅ 需要复用时先把数据落成列表再生成表达式。6. 把all(nums)当成「都非空」。❌all([1, 0, 2])期望True——0是假值结果是False。 ✅ 想判断「都不是None」就写all(n is not None for n in nums)真值判断和存在性判断是两件事。7. 混淆not any(...)与all(...)。❌not any(x 5 for x in data)和all(x 5 for x in data)当成一回事 —— 前者是「没有大于 5 的」后者是「全部大于 5 的」。 ✅ 记德摩根律not any(p)等价于all(not p)而不是all(p)。8. 用any()/all()替代or/and来取值。❌port any([config.get(port), 8080])—— 期望得到端口号实际得到True。 ✅ 取值用orport config.get(port) or 8080any/all只产出布尔结论。总结表达式结果原因any([])False没有任何正例all([])True找不到反例空真any([0, , None])False全是假值all([1, 0, 2])False0是假值any([0, 1])True不是1只返回布尔结论all(x 0 for x in gen)惰性 短路生成器表达式逐个求值找到反例即停any(s.find(w) for w in ws)恒为Truefind的-1是真值not any(p)等价于all(not p)德摩根律这两个函数真正的难点不在用法而在两处「不符合直觉」空序列的返回值和判断依据是真值而不是存在性。把all([]) is True当成一条必须记住的事实把「我要判断的是真值还是非空/非 None」当成每次使用前的自问这两类坑就基本能躲开。剩下的就只是一条好习惯——条件写成生成器表达式别写列表。