Python 函数式编程 教学笔记

函数式编程: 在 Python 中,函数式编程强调使用函数和表达式来进行数据处理,避免显式的循环和临时变量。常用的函数式编程工具包括高阶函数(如 map()filter())和**推导式**(列表、集合、字典推导式)等。它们可以让代码更简洁明了,提高可读性和开发效率。

lambda 表达式(匿名函数)

lambda 表达式用于创建匿名函数,其语法为 lambda 参数: 表达式,会返回一个函数对象。它通常用于临时定义简单的函数。示例如下

1
2
3
4
5
6
7
8
9
10
11
12
# 传统函数定义
def add(x, y):
return x + y

print(add(3, 5)) # 8

# 使用 lambda 表达式
f = lambda x, y: x + y
print(f(3, 5)) # 8

# 立即调用 lambda(不赋名)
print((lambda x, y: x * y)(3, 5)) # 15

注意:

  • lambda 表达式只能包含一个表达式,不能写赋值语句或多行逻辑。如果函数逻辑较复杂,仍然建议使用 def 定义函数。

map() 函数

map(function, iterable) 接收一个函数(可接受匿名函数)和一个可迭代对象,将函数依次作用到每个元素上,返回一个迭代器

例如,计算列表中每个数的平方:

1
2
3
4
5
6
7
8
nums = [1, 2, 3, 4, 5]

def square(x):
return x * x

# 使用 map 对每个元素求平方
result = map(square, nums)
print(list(result)) # [1, 4, 9, 16, 25]

注意:在 Python 3 中,map() 返回的是一个迭代器,需要用 list()tuple() 转换为列表才能查看所有结果。我们也可以使用 lambda 表达式简化函数定义:

1
2
3
4
5
6
7
8
9
#lambda 常与 map()、filter() 等函数一起使用。
nums = [1, 2, 3, 4, 5]
# 使用 lambda 和 map
squares = list(map(lambda x: x * x, nums))
print(squares) # [1, 4, 9, 16, 25]

nums1 = [1, 2, 3, 4 , nums]
doubles = list(map(lambda x: x * 2, nums1))
print(doubles) # [2, 4, 6, 8]

输出

1
2
[1, 4, 9, 16, 25]
[2, 4, 6, 8, [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]]

如果有多个可迭代对象传入,map() 会并行取各可迭代对象的元素作为参数传给函数,直到最短的可迭代对象耗尽。

示例 1:基础并行计算

1
2
3
4
5
6
7
# 计算三个列表中对应位置元素的和
nums1 = [1, 2, 3]
nums2 = [10, 20, 30]
nums3 = [100, 200, 300]

result = map(lambda x, y, z: x + y + z, nums1, nums2, nums3)
print(list(result)) # 输出: [111, 222, 333]

示例 2:不等长序列处理

1
2
3
4
5
6
letters = ['a', 'b', 'c']
numbers = [1, 2]

# 自动以最短的序列(numbers)为准
combined = map(lambda l, n: f"{l}{n}", letters, numbers)
print(list(combined)) # 输出: ['a1', 'b2'] (c被忽略)

示例 3:类型混合操作

1
2
3
4
5
6
7
8
names = ["Alice", "Bob"]
ages = [25, 30]
templates = ["{} is {} years old", "Name: {}, Age: {}"]

formatted = map(lambda n, a, t: t.format(n, a), names, ages, templates)
print(list(formatted))
# 输出:
# ['Alice is 25 years old', 'Name: Bob, Age: 30']

示例 4:模拟内置函数 zip()

1
2
3
4
5
6
# 用 map 实现类似 zip 的功能
keys = ['name', 'age']
values = ['Alice', 25]

dict_pairs = map(lambda k, v: (k, v), keys, values)
print(dict(dict_pairs)) # 输出: {'name': 'Alice', 'age': 25}

filter() 函数

filter(function, iterable)

  • 用于过滤序列。
  • 它也接收一个函数和一个可迭代对象,将函数作用到每个元素,保留返回值为 True 的元素.
  • 返回一个迭代器。

例如,从列表中过滤出偶数:

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

# 使用 filter 和 lambda 筛选偶数
evens = filter(lambda x: x % 2 == 0, nums)
print(list(evens)) # [2, 4, 6]

同样地,在 Python 3 中,filter() 返回迭代器,需要 list() 转换为列表。若 functionNonefilter() 则会去掉序列中所有值为假(如 None, 0, '' 等)的元素。例如:

1
2
3
4
data = ["Alice", "", "Bob", None, "Charlie", ""]
# 去除空字符串和 None
cleaned = list(filter(None, data))
print(cleaned) # ['Alice', 'Bob', 'Charlie']

三元表达式(Ternary Operator)

Python 的三元表达式(也称为条件表达式)提供了一种简洁的方式来根据条件选择两个值中的一个。它的基本语法如下:


基本语法

1
value_if_true if condition else value_if_false
  • 执行逻辑
    • 如果 conditionTrue,返回 value_if_true
    • 如果 conditionFalse,返回 value_if_false

示例 1:基本用法

1
2
3
4
5
6
x = 10
y = 20

# 使用三元表达式选择较大的值
max_value = x if x > y else y
print(max_value) # 输出: 20

示例 2:在赋值中使用

1
2
3
age = 18
status = "成年" if age >= 18 else "未成年"
print(status) # 输出: "成年"

示例 3:与函数返回值结合

1
2
3
4
5
def is_even(num):
return True if num % 2 == 0 else False

print(is_even(4)) # 输出: True
print(is_even(5)) # 输出: False

示例 4:嵌套三元表达式(不推荐,可读性差)

1
2
3
4
5
6
7
x = 10
y = 20
z = 30

# 嵌套三元表达式(可读性较低)
result = "x最大" if x > y and x > z else ("y最大" if y > z else "z最大")
print(result) # 输出: "z最大"

注意:嵌套三元表达式会降低代码可读性,建议改用 if-elif-else 结构。


示例 5:在列表推导式中使用

1
2
3
4
numbers = [1, 2, 3, 4, 5]
# 将偶数加倍,奇数保持不变
processed = [x * 2 if x % 2 == 0 else x for x in numbers]
print(processed) # 输出: [1, 4, 3, 8, 5]

示例 6:在Lambda匿名函数中使用

1
is_positive = lambda x: True if x > 0 else False

三元表达式 vs 普通 if-else

场景 三元表达式 普通 if-else
简单条件赋值 value = a if cond else b if cond: value = a else: value = b
多分支条件 可嵌套(但不推荐) 使用 elif 更清晰
代码简洁性 更紧凑 更冗长
可读性 简单条件时更直观 复杂逻辑时更易读

推导式(列表/集合/字典推导式)

推导式是一种简洁生成集合类型数据的表达式。常见的有列表推导式、集合推导式和字典推导式。

列表推导式

列表推导式(list comprehension)的基本形式如下,它可以生成一个新列表:

1
[表达式 for 变量 in 可迭代对象 if 条件]

示例

1
2
3
4
5
6
7
8
nums = [1, 2, 3, 4, 5]
# 计算平方,生成列表
squares = [x * x for x in nums]
print(squares) # [1, 4, 9, 16, 25]

# 带条件过滤:仅保留偶数的平方
even_squares = [x * x for x in nums if x % 2 == 0]
print(even_squares) # [4, 16]

上面两个例子分别对应对每个元素操作和带条件的操作。

列表推导式可以包含多个 for 子句,从而实现嵌套循环。

例如,将二维列表拍平:

1
2
3
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flatten = [num for row in matrix for num in row]
print(flatten) # [1, 2, 3, 4, 5, 6, 7, 8, 9]

小结: 官方教程指出,使用列表推导式可以在一行内创建列表,代码更加简洁易读。

集合推导式

集合推导式和列表推导式类似,使用 {} 来生成集合(去重且无序):

1
2
3
nums = [1, 2, 2, 3, 3, 4]
unique_set = {x for x in nums}
print(unique_set) # {1, 2, 3, 4}

如果需要按照顺序去重,可以先使用集合去除重复元素,然后转换为列表。不过要注意,set 会改变元素顺序。如果要保持原顺序去重,可使用字典(见后文)或循环处理。

字典推导式

字典推导式使用 {键: 值 for ...} 形式来生成字典。例如,将列表生成数字平方的字典:

1
2
3
nums = range(5)  # 0, 1, 2, 3, 4
square_dict = {x: x * x for x in nums}
print(square_dict) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

也可以结合条件或解包键值对。例如,将一组键值元组转换为字典,只包含值大于 1 的项:

1
2
3
pairs = [('a', 0), ('b', 2), ('c', 3)]
filtered_dict = {k: v for (k, v) in pairs if v > 1}
print(filtered_dict) # {'b': 2, 'c': 3}

推导式使用语法简单,且不需要手动创建并填充新集合,从而减少样板代码。

实战案例:函数式编程在实际中与传统写法的对比

下面通过多个小案例展示函数式编程在日常任务中的实际效果与优势。


案例一:数据清洗 —— 去除空白与空值

目标:从原始字符串列表中去除首尾空白并删除空项(包括 None 和空字符串)。

传统写法

1
2
3
4
5
6
7
8
9
raw = [" Alice ", "Bob", "  ", "Charlie  ", "", None, "  David"]
cleaned = []
for s in raw:
if s: # 过滤 None 和空字符串
stripped = s.strip()
if stripped:
cleaned.append(stripped)
print(cleaned)
# 输出 ['Alice', 'Bob', 'Charlie', 'David']

函数式写法(推荐使用列表推导式):

1
2
3
cleaned = [s.strip() for s in raw if s and s.strip()]
print(cleaned)
# 输出 ['Alice', 'Bob', 'Charlie', 'David']

也可使用 filter()map(),但列表推导式更简洁直观。


案例二:文本处理 —— 拆分句子并统计单词数

目标:将句子拆分为单词并统计每句单词数量。

传统写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sentences = ["Hello world", "Python is great", " Functional Programming"]
words_list = []
word_counts = []

for s in sentences:
s = s.strip()
if not s:
continue
words = s.split()
words_list.append(words)
word_counts.append(len(words))

print(words_list) # [['Hello', 'world'], ['Python', 'is', 'great'], ['Functional', 'Programming']]
print(word_counts) # [2, 3, 2]

函数式写法 1(列表推导式):

1
2
words_list = [s.strip().split() for s in sentences if s.strip()]
word_counts = [len(words) for words in words_list]

函数式写法 2map() + filter()):

1
2
3
sentence_stripped = filter(None, map(str.strip, sentences))
word_counts = list(map(lambda s: len(s.split()), sentence_stripped))
print(word_counts) # [2, 3, 2]

案例三:列表去重 —— 保留顺序或使用集合

目标:去除重复元素,尽可能保留原顺序。

传统写法

1
2
3
4
5
6
nums = [3, 1, 2, 3, 2, 4, 1]
unique = []
for x in nums:
if x not in unique:
unique.append(x)
print(unique) # [3, 1, 2, 4]

函数式写法

  • 使用集合(不保序)

    1
    unique_set = list(set(nums))
  • 使用 dict.fromkeys()(保序推荐)

    1
    2
    unique_ordered = list(dict.fromkeys(nums))
    print(unique_ordered) # [3, 1, 2, 4]

这些对比示例表明,函数式写法往往可以用更少的代码行实现相同功能,并且结构清晰,一眼就能看出“对每个元素做什么操作”。

函数式编程常见技巧进阶


一、高阶函数:map、filter、sorted 等组合使用

1
2
3
4
5
6
7
8
9
10
# map(): 批量转换
nums = [1, 2, 3]
squared = list(map(lambda x: x**2, nums)) # [1, 4, 9]

# filter(): 条件筛选
evens = list(filter(lambda x: x % 2 == 0, nums)) # [2]

# sorted(): 自定义排序
pairs = [(1, 'Z'), (3, 'A'), (2, 'B')]
sorted_pairs = sorted(pairs, key=lambda x: x[1]) # [(3, 'A'), (2, 'B'), (1, 'Z')]

二、列表与字典操作的函数式写法

1
2
3
4
5
6
7
# 列表元素统一处理(大写)
names = ["Alice", "Bob", "Charlie"]
upper_names = [(lambda s: s.upper())(name) for name in names] # ['ALICE', 'BOB', 'CHARLIE']

# 字典值转换处理
data = {'a': 1, 'b': 2}
transformed = {k: (lambda v: v * 10)(v) for k, v in data.items()} # {'a': 10, 'b': 20}

三、闭包与延迟计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 闭包:根据参数生成不同幂函数
def power_maker(n):
return lambda x: x ** n

square = power_maker(2)
cube = power_maker(3)
print(square(4)) # 16
print(cube(3)) # 27

# 延迟执行:常用于回调、按钮操作
button_actions = {
"add": lambda: print("执行加法"),
"exit": lambda: print("退出程序")
}
button_actions["add"]() # 执行加法

四、条件逻辑的函数式写法

1
2
3
4
5
6
7
8
9
10
11
# 简单条件表达
grade = lambda score: "及格" if score >= 60 else "不及格"
print(grade(75)) # 及格

# 多分支条件处理
category = lambda age: (
"儿童" if age < 12 else
"青少年" if age < 18 else
"成人"
)
print(category(15)) # 青少年

五、特殊参数与函数定义技巧

1
2
3
4
5
6
7
8
# lambda 支持默认参数
adder = lambda x, y=10: x + y
print(adder(5)) # 15
print(adder(5, 20)) # 25

# 支持可变参数
processor = lambda *args, **kwargs: f"收到位置参数: {args}, 关键字参数: {kwargs}"
print(processor(1, 2, a=3)) # 收到位置参数: (1, 2), 关键字参数: {'a': 3}

何时使用 lambda

场景 推荐使用 不推荐使用
简单数学运算 map(lambda x: x*2, lst) ❌ 复杂公式
临时回调函数 button.click(lambda: ...) ❌ 多步骤业务逻辑
排序/过滤键 sorted(data, key=lambda x: x[1]) ❌ 需要描述性名称时
函数参数简化 functools.reduce(lambda a,b: a+b, lst) ❌ 需要类型提示时

函数式编程的例题


示例 1:从列表中提取偶数并平方

1
2
3
nums = [1, 2, 3, 4, 5, 6]
result = list(map(lambda x: x**2, filter(lambda x: x % 2 == 0, nums)))
print(result) # [4, 16, 36]

示例 2:提取字符串中以“a”开头的单词

1
2
3
text = "apple banana apricot cherry avocado"
result = list(filter(lambda w: w.startswith("a"), text.split()))
print(result) # ['apple', 'apricot', 'avocado']

示例 3:字典键值批量转换(键大写,值乘以10)

1
2
3
data = {"x": 1, "y": 2}
result = {k.upper(): v * 10 for k, v in data.items()}
print(result) # {'X': 10, 'Y': 20}

示例 4:字符串列表转为单词列表(去空白+小写)

1
2
3
lines = [" Hello World  ", "  PYTHON  IS  Fun "]
words = [word.lower() for line in lines for word in line.strip().split()]
print(words) # ['hello', 'world', 'python', 'is', 'fun']

示例 5:生成指定幂函数的列表(闭包)

1
2
3
4
5
def power_makers(n_list):
return [lambda x, n=n: x**n for n in n_list]

powers = power_makers([1, 2, 3])
print([f(2) for f in powers]) # [2, 4, 8]

示例 6:对字符串按长度排序(忽略空白)

1
2
3
words = ["  banana", "apple", " cherry "]
result = sorted(words, key=lambda s: len(s.strip()))
print(result) # ['apple', ' cherry ', ' banana']

示例 7:从嵌套列表中提取所有数字

1
2
3
nested = [[1, 2], [3, 4], [5]]
flat = [x for sublist in nested for x in sublist]
print(flat) # [1, 2, 3, 4, 5]

示例 8:组合两个列表成字典(忽略 None 值)

1
2
3
4
keys = ["a", "b", "c"]
values = [1, None, 3]
result = {k: v for k, v in zip(keys, values) if v is not None}
print(result) # {'a': 1, 'c': 3}

示例 9:延迟计算表达式序列(惰性生成器)

1
2
3
expressions = [lambda x=i: x**2 for i in range(5)]
for f in expressions:
print(f(), end=" ") # 0 1 4 9 16

示例 10:多条件筛选数值

1
2
3
nums = range(20)
filtered = list(filter(lambda x: x % 2 == 0 and x % 3 != 0, nums))
print(filtered) # [2, 4, 8, 10, 14, 16]

示例 11:提取唯一单词并按字母排序

1
2
3
text = "apple banana apple orange Banana"
words = sorted(set(map(str.lower, text.split())))
print(words) # ['apple', 'banana', 'orange']

示例 12:统计每类词频(忽略大小写)

1
2
3
4
from collections import Counter
text = "Dog cat dog CAT fish"
counts = Counter(map(str.lower, text.split()))
print(dict(counts)) # {'dog': 2, 'cat': 2, 'fish': 1}

示例 13:对二维矩阵按列求和

1
2
3
matrix = [[1, 2, 3], [4, 5, 6]]
col_sum = list(map(sum, zip(*matrix)))
print(col_sum) # [5, 7, 9]

示例 14:动态生成带标签的打印函数(闭包)

1
2
3
4
5
def labeler(tag):
return lambda msg: f"[{tag.upper()}] {msg}"

warn = labeler("warn")
print(warn("Low battery")) # [WARN] Low battery

示例 15:按条件映射标签(分级)

1
2
3
scores = [45, 78, 90]
labels = list(map(lambda s: "优" if s >= 85 else "良" if s >= 60 else "差", scores))
print(labels) # ['差', '良', '优']

函数式编程的优势

  • 减少样板代码: 使用 mapfilter 和推导式时,不需要先创建空列表再循环填充,省去了多行循环模板代码。
  • 提高代码简洁度: 许多操作可以在一行表达式中完成,使得代码更简洁、一目了然。
  • 加快开发速度: 由于代码量减少,开发、调试和维护都更高效。开发者能专注于“做什么”而非“如何做”。
  • 可读性更强: 链式使用内置函数和推导式能够清楚表达数据转化逻辑,代码意图直观易懂。例如,一行推导式就可以展示过滤、映射、拆分等步骤。
  • 易于组合和复用: 许多函数式工具产生迭代器,可以方便地与其他工具(如 itertools)组合,编写复杂逻辑时保持简洁。

综上所述,掌握 mapfilterlambda 和各种推导式,可以帮助 Python 开发者编写更简洁、可读性更高的代码,大大减少样板工作,提高开发效率。