filter
filter() 用一个判断函数检查可迭代对象中的每个元素,只保留判断结果为真的元素。它适合表达“从一批数据中筛出符合条件的项”,是 Python 中常见的高阶函数。
本文使用 Python 3.10 及以上版本。读完后,你应该能够正确传入谓词函数、理解惰性迭代,并在 filter()、列表推导式和普通循环之间做出清晰选择。
基本语法
filter(function, iterable)
function:接收一个元素并返回真值的函数,也叫谓词函数;iterable:列表、元组、字符串、生成器等可迭代对象。
filter() 返回一个 filter 迭代器,而不是列表。需要查看或重复使用结果时,可以显式转换为列表或元组:
def is_even(number):
return number % 2 == 0
numbers = [1, 2, 3, 4, 5, 6]
even_numbers = filter(is_even, numbers)
print(list(even_numbers)) # [2, 4, 6]
传入函数对象时写 is_even;写 is_even(number) 则是在当前时刻调用函数,得到一个布尔值,不能作为 filter() 的回调。
谓词函数与 lambda
判断逻辑很短时,可以使用 lambda:
prices = [29, 120, 75, 230]
expensive = filter(lambda price: price >= 100, prices)
print(list(expensive)) # [120, 230]
如果条件需要复用、包含多个分支,或者希望在调试时看到有意义的函数名,应使用 def:
def is_valid_username(name):
return name and name.isidentifier() and len(name) <= 20
names = ["alice", "", "张三", "name-with-dash"]
valid_names = list(filter(is_valid_username, names))
print(valid_names) # ['alice', '张三']
谓词函数应专注于判断。不要在其中修改外部列表、写文件或发送请求,否则筛选过程的副作用会让代码难以预测。
filter() 的惰性
创建 filter 对象时,谓词函数还没有执行;只有迭代结果时才会逐项计算:
def report(number):
print(f"检查 {number}")
return number > 2
result = filter(report, [1, 2, 3, 4])
print("filter 已创建")
print(next(result)) # 输出“检查 1”“检查 2”“检查 3”,然后输出 3
这种惰性有两个重要影响:
- 可以处理很大的数据集,避免一次性创建所有筛选结果;
- 迭代器只能顺序消费一次,转换为列表后就不会自动重置。
active_users = filter(lambda user: user["active"], [
{"name": "A", "active": True},
{"name": "B", "active": False},
])
print(list(active_users)) # [{'name': 'A', 'active': True}]
print(list(active_users)) # []
如果需要多次遍历,提前物化结果:
active_users = list(filter(lambda user: user["active"], users))
省略判断函数:按真值筛选
当 function 传入 None 时,filter() 会保留真值为真的元素,效果类似于对每个元素调用 bool():
values = [0, 1, "", "python", None, [], [1, 2], False, True]
truthy_values = list(filter(None, values))
print(truthy_values) # [1, 'python', [1, 2], True]
常见的假值包括 None、False、数字 0、空字符串、空列表、空元组和空字典。对象是否为假值由其 __bool__() 或 __len__() 决定。
filter(None, values) 适合“删除所有空值”的场景,但它也会删除合法的 0 或 False。如果只想删除 None,请写出明确条件:
values = [0, None, 1, False]
without_none = list(filter(lambda value: value is not None, values))
print(without_none) # [0, 1, False]
与列表推导式比较
以下写法等价:
numbers = [1, 2, 3, 4, 5]
filtered = list(filter(lambda number: number % 2, numbers))
comprehension = [number for number in numbers if number % 2]
通常可以这样选择:
| 场景 | 推荐写法 |
|---|---|
| 简单条件,结果马上要用 | 列表推导式 |
| 已有命名谓词,或要组合高阶函数 | filter() |
| 结果很大,需要按需消费 | 保留 filter 迭代器 |
| 需要多个分支、日志或异常处理 | for 循环 |
列表推导式通常更符合 Python 的阅读习惯,filter() 的优势在于可以传入可复用的判断函数,并自然地和 map()、sorted() 等函数组合。
常见数据筛选
筛选字符串
words = ["Python", "go", "", "Dart", "JS"]
long_words = list(filter(lambda word: len(word) >= 3, words))
print(long_words) # ['Python', 'Dart']
筛选字典记录
orders = [
{"id": 1, "status": "paid", "amount": 80},
{"id": 2, "status": "paid", "amount": 180},
{"id": 3, "status": "cancelled", "amount": 300},
]
large_paid_orders = list(filter(
lambda order: order["status"] == "paid" and order["amount"] >= 100,
orders,
))
print(large_paid_orders) # [{'id': 2, 'status': 'paid', 'amount': 180}]
筛选字典的键和值
字典迭代时默认产生键。要筛选值或键值对,应明确调用 .values() 或 .items():
scores = {"Alice": 92, "Bob": 58, "Cindy": 76}
passed_names = list(filter(lambda name: scores[name] >= 60, scores))
passed_scores = list(filter(lambda score: score >= 60, scores.values()))
passed_items = list(filter(lambda item: item[1] >= 60, scores.items()))
print(passed_names) # ['Alice', 'Cindy']
print(passed_scores) # [92, 76]
print(passed_items) # [('Alice', 92), ('Cindy', 76)]
与其他高阶函数组合
filter() 返回的迭代器可以作为 map() 的输入,先筛选再转换:
from collections.abc import Iterable
def normalize_positive(values: Iterable[int]) -> list[int]:
positive = filter(lambda value: value > 0, values)
return list(map(abs, positive))
print(normalize_positive([-3, 0, 2, -1])) # [3, 2]
也可以把筛选结果直接交给 sorted():
students = [
{"name": "Alice", "score": 88, "active": True},
{"name": "Bob", "score": 95, "active": False},
{"name": "Cindy", "score": 91, "active": True},
]
ranking = sorted(
filter(lambda student: student["active"], students),
key=lambda student: student["score"],
reverse=True,
)
print(ranking[0]["name"]) # Cindy
组合较多时,给中间结果命名通常比写一行嵌套表达式更便于调试。
类型提示
谓词可以用 Callable 描述,输入可使用 Iterable 以支持列表、元组和生成器:
from collections.abc import Callable, Iterable, Iterator
def select(
values: Iterable[int],
predicate: Callable[[int], bool],
) -> Iterator[int]:
return filter(predicate, values)
result = select(range(6), lambda value: value % 2 == 0)
print(list(result)) # [0, 2, 4]
返回类型是 Iterator,提醒调用者结果具有惰性;如果函数的接口承诺返回 list[int],就在函数内部显式调用 list()。
常见错误与边界
把结果对象当成列表
result = filter(lambda value: value > 0, [-1, 0, 1])
# result[0] # TypeError:filter 不支持下标访问
print(next(result)) # 1
需要下标、长度或多次访问时,转换为列表:result = list(result)。
谓词返回的不是布尔值
谓词不必显式返回 True 或 False,任意对象都会按真值判断。但为了清晰,复杂条件最好显式写出比较结果:
items = [{"name": "A"}, {"name": ""}]
has_name = list(filter(lambda item: item["name"], items))
在筛选时修改源数据
不要在谓词中删除源列表元素,也不要依赖源列表在迭代期间被修改。先完成筛选,再执行修改,或者创建新的结果列表。
异常不会被吞掉
谓词访问不存在的键、执行除零等操作时,异常会在迭代结果的瞬间抛出:
records = [{"score": 80}, {}]
result = filter(lambda record: record["score"] >= 60, records)
# list(result) # 第二条记录会触发 KeyError
应在谓词中使用 dict.get()、预校验数据,或在普通循环中按业务需求处理异常。
练习
- 使用
filter()保留列表中能被 3 整除的数字。 - 分别用
filter(None, values)和value is not None,比较它们处理0、False的差异。 - 从订单字典列表中筛选出金额大于 500 且状态为
paid的订单。 - 组合
filter()与map(),把正数筛出后转换成字符串。 - 把一个包含三层嵌套
filter()的表达式改写成命名谓词和中间变量。
小结
filter(function, iterable)保留谓词结果为真的元素。- 它返回惰性迭代器,只有被消费时才执行判断,且只能顺序消费一次。
function为None时按元素真值筛选;只删除None时要使用is not None。- 列表推导式通常更直观,命名谓词和高阶函数组合是
filter()的主要优势。 - 谓词应尽量无副作用;需要复杂分支、异常处理或多个状态时,普通
for循环更清晰。