在处理列表、元组这类序列数据时,筛选符合条件的元素几乎是绕不开的操作。Python提供了内置的filter()函数专门干这件事:给它一个判断函数和一个序列,它会把判断结果为真的元素挑出来。很多初学者第一次接触filter()时容易被它的返回值和参数写法搞晕,这篇文章就把filter()的语法、原理、常见用法和易错点一次性讲清楚。

filter()函数的基本语法与工作原理
filter()的函数签名很简单:filter(function, iterable),第一个参数是一个函数,第二个参数是一个可迭代对象。它的作用是遍历iterable中的每个元素,把元素作为参数传给function,如果函数返回真值(True、非零数字、非空字符串等都算真值),这个元素就会被保留下来,否则被丢弃。
需要特别注意的是,Python 3中的filter()返回的不是列表,而是一个filter对象,它是一个惰性求值的迭代器。也就是说,元素并不会在调用filter()的那一刻就被过滤出来,而是在你实际遍历它的时候才逐个计算。这样做的好处是节省内存,处理超大数据集时不会一次性把结果全部装进内存。但如果你想直接查看或重复使用过滤结果,就得把它转成列表:
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 定义判断函数:判断是否为偶数
def is_even(n):
return n % 2 == 0
# 使用filter过滤出所有偶数
result = filter(is_even, numbers)
# filter对象是迭代器,转成列表才能查看完整内容
print(list(result)) # 输出: [2, 4, 6, 8, 10]
这里有一个新手特别容易踩的坑:filter对象只能被遍历一次。如果你对同一个filter对象执行两次list()转换,第二次会得到一个空列表,因为迭代器在第一次遍历时已经耗尽了。所以在实际开发中,建议把filter()的结果立即转成列表保存,需要多次使用时就复用这个列表,而不是重复消费迭代器。
另一个常见的语法错误是给函数参数加上了括号。注意filter(is_even, numbers)传的是函数本身,如果你写成filter(is_even(), numbers),就变成了传函数的返回值,is_even需要接收参数n,直接调用会报TypeError。只要记住口诀:传函数名,不加括号,调用交给filter内部完成。
结合lambda表达式与None参数的实用写法
如果过滤逻辑很简单,专门定义一个函数显得有点啰嗦,这时候lambda表达式就派上用场了。lambda可以创建匿名的单行函数,配合filter()能让代码更紧凑:
words = ['apple', '', 'banana', '', 'cherry', 'dog', 'elephant'] # 用lambda筛选长度大于4的单词 long_words = filter(lambda w: len(w) > 4, words) print(list(long_words)) # 输出: ['apple', 'banana', 'cherry', 'elephant'] # 过滤出1到100之间能被7整除的数 divisible = filter(lambda x: x % 7 == 0, range(1, 101)) print(list(divisible)) # 输出: [7, 14, 21, ..., 98]
filter()还有一个不太为人熟知的特殊用法:当第一个参数传None时,filter会直接根据元素本身的真假来过滤,等价于lambda x: x。这个特性在清洗数据、剔除空值时非常方便:
data = [0, 1, '', 'hello', None, [], [1, 2], {}, False, 'world']
# 剔除所有假值元素
cleaned = filter(None, data)
print(list(cleaned)) # 输出: [1, 'hello', [1, 2], 'world']
可以看到,0、空字符串、None、空列表、空字典、False这些假值全部被剔除了。不过要提醒一点:如果你只想剔除None而保留0和空字符串,就不能用filter(None, ...)了,得老老实实写filter(lambda x: x is not None, data),因为None参数的过滤是针对所有假值,而不是只针对None这个对象。
在处理实际业务数据时,比如一个存放用户信息的字典列表,filter配合lambda同样适用。假设要筛选出年龄大于等于18岁的用户,写法如下:
users = [
{'name': '张三', 'age': 22},
{'name': '李四', 'age': 15},
{'name': '王五', 'age': 30},
{'name': '赵六', 'age': 17},
]
adults = filter(lambda u: u['age'] >= 18, users)
print(list(adults))
# 输出: [{'name': '张三', 'age': 22}, {'name': '王五', 'age': 30}]
filter()与列表推导式的对比及选择建议
熟悉Python的人会发现,上面这些例子几乎都可以用列表推导式改写,例如[n for n in numbers if n % 2 == 0]和filter(is_even, numbers)效果完全一样。那么两种写法该如何选择呢?我们从可读性、性能和灵活性三个角度来分析。
从可读性上看,列表推导式的条件和表达式都写在一对方括号里,逻辑一目了然,尤其适合简单条件;filter()配合lambda的写法在条件复杂时也不错,但如果判断函数是外部定义的具名函数,filter反而更能体现意图,因为函数名本身就能说明过滤规则。从性能上看,两者差距不大,在简单场景下列表推导式通常略快一点,因为省去了函数调用的开销。从灵活性上看,列表推导式只能生成列表,而filter()返回迭代器,配合生成器的思路处理大数据流更省内存。
| 对比维度 | filter()函数 | 列表推导式 |
|---|---|---|
| 返回类型 | 迭代器(惰性求值) | 列表(立即求值) |
| 内存占用 | 低,适合大数据 | 高,一次性生成 |
| 复用已有函数 | 方便,直接传函数名 | 需要内联条件表达式 |
| 可读性 | 配合具名函数时清晰 | 简单条件下更直观 |
实践中有一个简单的选择标准:如果过滤逻辑本身已经是现成的函数(比如内置的str.strip、自定义的校验函数),或者需要处理的数据量很大、希望惰性计算,就用filter();如果条件简单且需要立即得到列表,列表推导式更顺手。另外在Python 2中filter()返回的是列表,Python 3改成了迭代器,这也是老代码迁移时需要注意的兼容性差异。
最后总结几个要点:filter()接收函数和可迭代对象两个参数,函数返回真值的元素被保留;返回的filter对象是迭代器,用完即耗尽,建议及时转成列表;函数参数只传函数名不加分号调用;传None可以快速剔除假值。掌握这些细节后,序列过滤这个高频操作就能写得既简洁又不容易出错。
Python filter函数序列过滤lambda表达式修改时间:2026-09-07 14:36:43