Python高级特性:filter() 函数完全指南

2 阅读8分钟

Python高级特性:filter() 函数完全指南

优雅地过滤序列元素

前言

filter() 是 Python 的内置高阶函数,用于过滤序列中的元素。它接收一个判断函数和一个可迭代对象,返回一个包含所有使函数返回 True 的元素的迭代器。与 map() 不同,filter() 的核心作用是筛选而非转换。

本文将系统讲解 filter() 的语法、惰性求值特性、与列表推导式的对比、以及素数筛选、回文数检测等实用案例,帮助你掌握这一函数式编程利器。

📚 本文内容基于道满PythonAI - filter() 函数详解与实用案例


一、filter() 函数基础

1.1 基本语法

filter(function, iterable)
参数说明
function判断函数,接收一个参数,返回布尔值(True/False
iterable可迭代对象(列表、元组、字符串等)
返回值迭代器(Python 3),包含所有使函数返回 True 的元素

1.2 filter() 与 map() 的区别

函数作用返回结果
map()将函数应用于每个元素函数处理后的新值
filter()根据条件筛选元素原序列中符合条件的元素
numbers = [1, 2, 3, 4, 5]

# map: 每个元素都转换
squared = list(map(lambda x: x**2, numbers))
print(squared)  # [1, 4, 9, 16, 25]

# filter: 只保留符合条件的元素
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens)    # [2, 4]

二、基本使用示例

2.1 过滤奇数(保留奇数)

def is_odd(n):
    """判断是否为奇数"""
    return n % 2 == 1

numbers = [1, 2, 4, 5, 6, 9, 10, 15]
result = list(filter(is_odd, numbers))
print(result)  # [1, 5, 9, 15]

2.2 过滤空字符串和 None

def not_empty(s):
    """判断字符串是否非空(去除空格后)"""
    return s and s.strip()

data = ['A', '', 'B', None, 'C', ' ']
result = list(filter(not_empty, data))
print(result)  # ['A', 'B', 'C']

2.3 使用 lambda 表达式简化

numbers = [1, 2, 3, 4, 5, 6]

# 过滤偶数
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens)  # [2, 4, 6]

# 过滤大于3的数
greater_than_3 = list(filter(lambda x: x > 3, numbers))
print(greater_than_3)  # [4, 5, 6]

三、惰性求值特性

filter() 返回的是一个迭代器,这意味着它是惰性计算的:

# 不会立即执行计算
filtered = filter(lambda x: x > 5, [3, 6, 2, 8])

print(filtered)  # <filter object at 0x...>

# 只有在需要时才会计算(手动调用next或转换为列表)
print(next(filtered))  # 6
print(next(filtered))  # 8
# print(next(filtered))  # 抛出StopIteration

# 转换为列表(一次性计算所有)
filtered = filter(lambda x: x > 5, [3, 6, 2, 8])
print(list(filtered))  # [6, 8]

📌 内存优势:惰性求值意味着filter()不会立即创建整个结果列表,对于大数据集可以显著节省内存


四、实用案例

4.1 埃拉托斯特尼筛法(素数筛选)

这是一个经典的素数生成算法,使用filter()动态筛选出素数:

def primes():
    """生成无限素数序列的生成器"""
    
    # 生成从3开始的奇数序列
    def _odd_iter():
        n = 1
        while True:
            n += 2
            yield n
    
    # 返回一个判断是否能被n整除的函数
    def _not_divisible(n):
        return lambda x: x % n != 0
    
    yield 2  # 第一个素数是2
    it = _odd_iter()  # 初始序列:3,5,7,9,11,...
    
    while True:
        n = next(it)  # 获取序列的第一个数(一定是素数)
        yield n
        # 用当前素数n过滤后续序列,留下不能被n整除的数
        it = filter(_not_divisible(n), it)

# 打印100以内的素数
print("100以内的素数:")
for prime in primes():
    if prime < 100:
        print(prime, end=' ')
    else:
        break
# 输出: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

4.2 筛选回文数

def is_palindrome(n):
    """判断一个数是否是回文数(正读反读相同)"""
    s = str(n)
    return s == s[::-1]

# 测试1-1000的回文数
palindromes = list(filter(is_palindrome, range(1, 1000)))
print("1~1000的回文数:", palindromes)
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, ..., 979, 989, 999]

# 测试验证
test_data = range(1, 200)
expected = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 
            101, 111, 121, 131, 141, 151, 161, 171, 181, 191]
if list(filter(is_palindrome, test_data)) == expected:
    print("测试成功!")
else:
    print("测试失败!")

4.3 筛选指定范围内的闰年

def is_leap_year(year):
    """判断是否为闰年"""
    return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)

years = range(2000, 2025)
leap_years = list(filter(is_leap_year, years))
print(f"2000-2024年的闰年: {leap_years}")
# [2000, 2004, 2008, 2012, 2016, 2020, 2024]

4.4 筛选列表中符合条件的字典元素

# 学生数据
students = [
    {"name": "Alice", "score": 85, "age": 20},
    {"name": "Bob", "score": 92, "age": 22},
    {"name": "Charlie", "score": 78, "age": 19},
    {"name": "Diana", "score": 95, "age": 21},
]

# 筛选成绩大于等于90分的学生
high_scorers = list(filter(lambda s: s["score"] >= 90, students))
print("高分学生:", high_scorers)
# [{'name': 'Bob', 'score': 92, 'age': 22}, {'name': 'Diana', 'score': 95, 'age': 21}]

# 筛选年龄大于20岁的学生
older_students = list(filter(lambda s: s["age"] > 20, students))
print("年龄大于20的学生:", older_students)
# [{'name': 'Bob', 'score': 92, 'age': 22}, {'name': 'Diana', 'score': 95, 'age': 21}]

五、现代Python中的替代方案

虽然 filter() 仍然有用,但在许多情况下,列表推导式生成器表达式可能更清晰、更Pythonic。

5.1 filter() vs 列表推导式

numbers = [3, 6, 2, 8, 1, 9]

# 使用filter
filtered_filter = list(filter(lambda x: x > 5, numbers))

# 等效的列表推导式
filtered_comp = [x for x in numbers if x > 5]

print(filtered_filter)  # [6, 8, 9]
print(filtered_comp)    # [6, 8, 9]

5.2 何时使用 filter()?

场景推荐方式原因
简单条件过滤列表推导式更直观、更Pythonic
过滤函数已定义且复杂filter()代码更简洁
处理大数据集filter()返回迭代器,节省内存
函数式编程风格filter()符合函数式范式

5.3 生成器表达式的内存优势

import sys

# 列表推导式:立即创建列表,占用内存
list_comp = [x for x in range(1000000) if x % 2 == 0]
print(f"列表内存: {sys.getsizeof(list_comp)} bytes")

# filter:返回迭代器,几乎不占内存
filter_iter = filter(lambda x: x % 2 == 0, range(1000000))
print(f"filter内存: {sys.getsizeof(filter_iter)} bytes")

# 生成器表达式:同样内存友好
gen_exp = (x for x in range(1000000) if x % 2 == 0)
print(f"生成器内存: {sys.getsizeof(gen_exp)} bytes")

六、性能考虑

方法内存效率适用场景
filter()高(惰性求值)大数据集、函数已定义
列表推导式中(立即创建列表)中小数据集、简单条件
生成器表达式高(惰性求值)大数据集、简单条件

💡 建议

  • 对于简单过滤,列表推导式通常更清晰
  • 对于大型数据集filter()或生成器表达式更节省内存
  • 如果需要多次使用过滤结果,转换为列表

七、练习:回文数筛选

def is_palindrome(n):
    """判断一个数是否是回文数"""
    s = str(n)
    return s == s[::-1]

# 测试1-1000的回文数
palindromes = list(filter(is_palindrome, range(1, 1000)))
print("1~1000的回文数:", palindromes)

# 测试验证
test_data = range(1, 200)
expected = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 
            101, 111, 121, 131, 141, 151, 161, 171, 181, 191]
if list(filter(is_palindrome, test_data)) == expected:
    print("测试成功!")
else:
    print("测试失败!")

八、扩展:itertools.filterfalse()

itertools模块提供了filterfalse()函数,它返回使函数返回False的元素:

from itertools import filterfalse

numbers = [1, 2, 3, 4, 5, 6]

# 过滤偶数(保留奇数)
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens)  # [2, 4, 6]

# filterfalse:保留使函数返回False的元素(即奇数)
odds = list(filterfalse(lambda x: x % 2 == 0, numbers))
print(odds)   # [1, 3, 5]

九、总结

知识点要点
基本语法filter(function, iterable)
返回值迭代器(惰性求值)
map区别map转换元素,filter筛选元素
与列表推导式列表推导式更Pythonic,filter内存效率更高
典型应用素数筛选、回文数、数据清洗
性能特点惰性求值,适合大数据集

核心要点

  1. filter() 根据判断函数筛选序列元素,返回迭代器
  2. 惰性求值特性使其内存效率高,适合大数据集
  3. ✅ 可与 lambda 表达式结合,实现简洁的内联过滤
  4. ✅ 简单过滤场景,列表推导式通常更清晰
  5. itertools.filterfalse() 可获取相反结果

掌握 filter() 函数,可以让你在处理序列数据时更加游刃有余,写出更具函数式风格的Python代码。


📚 相关推荐阅读


💡 Python 学习不走弯路!

体系化实战路线:基础语法 · 异步Web开发 · 数据采集 · 计算机视觉 · NLP · 大模型RAG实战
—— 全在 「道满PythonAI」


如果这篇文章对你有帮助,欢迎点赞、评论、收藏,你的支持是我持续分享的动力!🎉