跳到主要内容

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

这种惰性有两个重要影响:

  1. 可以处理很大的数据集,避免一次性创建所有筛选结果;
  2. 迭代器只能顺序消费一次,转换为列表后就不会自动重置。
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()、预校验数据,或在普通循环中按业务需求处理异常。

练习​

  1. 使用 filter() 保留列表中能被 3 整除的数字。
  2. 分别用 filter(None, values) 和 value is not None,比较它们处理 0、False 的差异。
  3. 从订单字典列表中筛选出金额大于 500 且状态为 paid 的订单。
  4. 组合 filter() 与 map(),把正数筛出后转换成字符串。
  5. 把一个包含三层嵌套 filter() 的表达式改写成命名谓词和中间变量。

小结​

  • filter(function, iterable) 保留谓词结果为真的元素。
  • 它返回惰性迭代器,只有被消费时才执行判断,且只能顺序消费一次。
  • function 为 None 时按元素真值筛选;只删除 None 时要使用 is not None。
  • 列表推导式通常更直观,命名谓词和高阶函数组合是 filter() 的主要优势。
  • 谓词应尽量无副作用;需要复杂分支、异常处理或多个状态时,普通 for 循环更清晰。