gao

导航

Python学习之函数式编程特性(map/filter/lambda)

Python 3 函数式编程特性(lambda / map / filter)

Python 不是纯函数式语言,但从一开始就吸收了函数式编程的核心思想——把函数当作一等公民,支持高阶函数、匿名函数、惰性求值等特性。这篇文档聚焦最常用的三个工具:lambdamapfilter,以及它们的替代写法和最佳实践。


一、lambda —— 匿名函数

1.1 基本语法

lambda 参数列表: 表达式

lambda 创建的是一个单行匿名函数,等价于用 def 定义一个只有 return 语句的函数:

# lambda 写法
square = lambda x: x ** 2

# 等价的 def 写法
def square(x):
    return x ** 2

# 调用方式完全一样
square(5)  # 25

1.2 多参数

add = lambda a, b: a + b
add(3, 7)       # 10

greet = lambda name, time: f"Good {time}, {name}!"
greet("Alice", "morning")  # 'Good morning, Alice!'

1.3 带默认参数

power = lambda base, exp=2: base ** exp
power(3)      # 9(使用默认 exp=2)
power(3, 3)   # 27

1.4 lambda 的典型使用场景

lambda 通常不会单独赋值给变量使用(那样不如直接写 def),它最大的价值是作为参数传入高阶函数,在需要一个临时小函数的地方"即用即弃":

# 排序时指定 key
students = [("Alice", 88), ("Bob", 95), ("Charlie", 82)]
students.sort(key=lambda s: s[1])   # 按分数排序
# [('Charlie', 82), ('Alice', 88), ('Bob', 95)]

# 按字符串的某个属性排序
words = ["banana", "pie", "Washington", "book"]
words.sort(key=lambda w: len(w))    # 按长度排序
# ['pie', 'book', 'banana', 'Washington']

words.sort(key=lambda w: w.lower()) # 不区分大小写排序
# ['banana', 'book', 'pie', 'Washington']

1.5 lambda 的限制

lambda 只能包含一个表达式,不能包含语句(赋值、if 块、for 循环等):

# 不能这样做:
# lambda x: if x > 0: return x else: return 0    # SyntaxError

# 可以用三元表达式替代:
safe_div = lambda a, b: a / b if b != 0 else float('inf')
safe_div(10, 2)   # 5.0
safe_div(10, 0)   # inf

二、map() —— 映射变换

2.1 基本语法

map(函数, 可迭代对象)

map 将函数依次应用到可迭代对象的每个元素上,返回一个惰性的 map 对象(迭代器),不会立即计算所有结果。

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

# 将每个元素平方
result = map(lambda x: x ** 2, numbers)
print(result)        # <map object at 0x...>(惰性的,还没计算)
list(result)         # [1, 4, 9, 16, 25](触发计算)

2.2 用具名函数更清晰

当函数逻辑稍复杂时,用 def 定义具名函数比塞 lambda 更易读:

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

def square(x):
    return x ** 2

list(map(square, numbers))   # [1, 4, 9, 16, 25]

2.3 传入多个可迭代对象

当传入多个可迭代对象时,函数会同时接收每个对象的对应元素:

a = [1, 2, 3]
b = [10, 20, 30]

list(map(lambda x, y: x + y, a, b))   # [11, 22, 33]

# 实际应用:合并两个列表的数据
names = ["Alice", "Bob", "Charlie"]
scores = [88, 95, 82]
list(map(lambda n, s: f"{n}: {s}", names, scores))
# ['Alice: 88', 'Bob: 95', 'Charlie: 82']

当多个可迭代对象长度不同时,map 会在最短的那个用完时停止:

list(map(lambda x, y: x + y, [1, 2, 3], [10, 20]))
# [11, 22](只有两个结果)

2.4 map 配合内置函数

map 不一定需要 lambda,任何函数都可以传入:

# 将字符串列表转为整数
str_nums = ["10", "20", "30"]
list(map(int, str_nums))              # [10, 20, 30]

# 全部转大写
words = ["hello", "world"]
list(map(str.upper, words))           # ['HELLO', 'WORLD']

# 保留两位小数
values = [3.14159, 2.71828, 1.41421]
list(map(lambda v: round(v, 2), values))  # [3.14, 2.72, 1.41]

2.5 实际应用示例

# 从字典列表中提取某个字段
users = [
    {"name": "Alice", "age": 30},
    {"name": "Bob", "age": 25},
    {"name": "Charlie", "age": 28},
]
names = list(map(lambda u: u["name"], users))
# ['Alice', 'Bob', 'Charlie']

# 批量处理文件路径
import os
paths = ["docs/report.pdf", "images/photo.jpg", "src/main.py"]
basenames = list(map(os.path.basename, paths))
# ['report.pdf', 'photo.jpg', 'main.py']

# 矩阵转置(配合 zip)
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
transposed = list(map(list, zip(*matrix)))
# [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

三、filter() —— 条件过滤

3.1 基本语法

filter(函数, 可迭代对象)

filter 将函数依次应用到每个元素,保留函数返回值为 True 的元素,同样返回一个惰性的迭代器。

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 筛选偶数
evens = filter(lambda x: x % 2 == 0, numbers)
list(evens)   # [2, 4, 6, 8, 10]

# 筛选大于 5 的数
bigger = filter(lambda x: x > 5, numbers)
list(bigger)  # [6, 7, 8, 9, 10]

3.2 过滤函数的返回值规则

传入的函数不一定要返回 True / False,Python 会按布尔值来判定——任何"真值"都保留,"假值"都过滤掉:

# 过滤掉 None 值
items = [0, 1, None, "", "hello", [], [1, 2], False, True]
truthy = list(filter(None, items))
# [1, 'hello', [1, 2], True]

# 传入 None 作为函数时,filter 直接用元素本身的真值来判断

3.3 实际应用示例

# 过滤掉空字符串
lines = ["hello", "", "world", "", "python", "" ]
non_empty = list(filter(None, lines))
# ['hello', 'world', 'python']

# 筛选满足条件的对象
files = ["report.pdf", "data.csv", "image.png", "notes.txt", "backup.pdf"]
pdfs = list(filter(lambda f: f.endswith(".pdf"), files))
# ['report.pdf', 'backup.pdf']

# 从字典列表中筛选
products = [
    {"name": "Widget", "price": 9.99, "in_stock": True},
    {"name": "Gadget", "price": 24.99, "in_stock": False},
    {"name": "Doohickey", "price": 4.99, "in_stock": True},
]
available = list(filter(lambda p: p["in_stock"], products))
# [{'name': 'Widget', ...}, {'name': 'Doohickey', ...}]

# 过滤掉异常值
sensor_data = [22.3, 999.9, 21.8, -1.0, 23.1, 999.9, 22.7]
valid = list(filter(lambda v: 0 <= v <= 100, sensor_data))
# [22.3, 21.8, 23.1, 22.7]

四、map + filter 组合使用

mapfilter 可以链式组合,形成数据处理管道:

# 先过滤,再映射
numbers = range(1, 21)

# 找出偶数的平方
result = map(lambda x: x ** 2,
             filter(lambda x: x % 2 == 0, numbers))
list(result)  # [4, 16, 36, 64, 100, 144, 196, 256, 324, 400]
# 实际场景:从用户列表中筛选活跃用户,提取邮箱
users = [
    {"email": "alice@test.com", "active": True},
    {"email": "bob@test.com", "active": False},
    {"email": "carol@test.com", "active": True},
    {"email": "dave@test.com", "active": False},
]

active_emails = list(
    map(lambda u: u["email"],
        filter(lambda u: u["active"], users))
)
# ['alice@test.com', 'carol@test.com']

五、reduce() —— 累积归约

reduce 不像 map / filter 那样是内置函数,它住在 functools 模块里,但同样是函数式编程的重要工具。

from functools import reduce

# 基本语法
reduce(函数, 可迭代对象, 初始值(可选))

reduce 依次将函数应用到累积值和当前元素上,最终将整个序列归约为单个值:

from functools import reduce

# 求和
nums = [1, 2, 3, 4, 5]
total = reduce(lambda acc, x: acc + x, nums)
# 15

# 求乘积
product = reduce(lambda acc, x: acc * x, nums)
# 120

# 找最大值
maximum = reduce(lambda a, b: a if a > b else b, nums)
# 5

reduce 的工作过程详解:

reduce(lambda acc, x: acc + x, [1, 2, 3, 4, 5])

步骤1: acc=1, x=2  → 1+2 = 3
步骤2: acc=3, x=3  → 3+3 = 6
步骤3: acc=6, x=4  → 6+4 = 10
步骤4: acc=10, x=5 → 10+5 = 15
结果: 15

带初始值:

# 初始值为 0
reduce(lambda acc, x: acc + x, [1, 2, 3], 0)   # 6

# 初始值为 1(乘法单位元)
reduce(lambda acc, x: acc * x, [1, 2, 3, 4], 1)  # 24

# 将列表拼成字典
pairs = [("a", 1), ("b", 2), ("c", 3)]
d = reduce(lambda acc, p: {**acc, p[0]: p[1]}, pairs, {})
# {'a': 1, 'b': 2, 'c': 3}

注意:Python 社区普遍认为 reduce 的可读性不如显式的 for 循环。简单的求和、求最大值等操作,优先使用内置函数 sum()max()min()。只在需要自定义归约逻辑时才考虑 reduce


六、列表推导式 vs map/filter —— 该用哪个

对同一个任务,列表推导式和 map / filter 通常都能实现。选择哪个取决于可读性:

场景 推荐写法 示例
简单映射 列表推导式 [x ** 2 for x in nums]
简单过滤 列表推导式 [x for x in nums if x > 0]
映射 + 过滤 列表推导式 [x ** 2 for x in nums if x > 0]
需要惰性求值 map / filter 处理大数据集时节省内存
已有现成函数 map map(int, str_list)
需要多可迭代对象并行 map map(f, a, b, c)
# 这两种写法等价,列表推导式更 Pythonic:
nums = [1, 2, 3, 4, 5]

# map/filter 写法
result = list(map(lambda x: x ** 2, filter(lambda x: x > 2, nums)))

# 列表推导式写法(推荐)
result = [x ** 2 for x in nums if x > 2]

核心建议: 在 Python 中,优先使用列表推导式(可读性更好)。在需要惰性求值处理大数据、或者已经有一个现成函数可以直接传入时,map / filter 才是更好的选择。


七、其他函数式工具

Python 的 functoolsitertools 模块提供了更多函数式编程工具,值得了解:

functools.partial —— 固定部分参数

from functools import partial

def power(base, exp):
    return base ** exp

square = partial(power, exp=2)
cube = partial(power, exp=3)

square(5)   # 25
cube(3)     # 27

itertools 常用工具

from itertools import chain, groupby, takewhile, dropwhile

# chain:拼接多个可迭代对象
list(chain([1, 2], [3, 4], [5]))      # [1, 2, 3, 4, 5]

# takewhile:从开头取元素,直到条件不满足
list(takewhile(lambda x: x < 5, [1, 3, 5, 2, 4]))  # [1, 3]

# dropwhile:从开头跳过元素,直到条件不满足
list(dropwhile(lambda x: x < 5, [1, 3, 5, 2, 4]))  # [5, 2, 4]

八、速查表

工具 作用 示例
lambda x: expr 创建匿名函数 lambda x, y: x + y
map(f, iterable) 对每个元素应用函数 map(str.upper, words)
filter(f, iterable) 保留使函数返回真值的元素 filter(lambda x: x > 0, nums)
reduce(f, iterable) 累积归约为单个值 reduce(lambda a, b: a + b, nums)
functools.partial 固定函数的部分参数 partial(pow, exp=2)
itertools.chain 拼接多个可迭代对象 chain(a, b, c)

posted on 2026-05-14 15:36  code0101  阅读(11)  评论(0)    收藏  举报