🐍 内置函数 — 函数式编程工具箱
🕐预计用时:2-3 小时 | 🎯目标:掌握 map/filter/reduce、zip/enumerate、sorted/reversed、any/all
📖 今日目录
1. map:批量转换
map(函数, 可迭代对象) — 对每个元素执行同一个函数,返回新结果。
# 传统写法
nums = [1, 2, 3, 4, 5]
squares = []
for n in nums:
squares.append(n ** 2)
print(squares) # [1, 4, 9, 16, 25]
# map 写法(一行搞定)
nums = [1, 2, 3, 4, 5]
squares = list(map(lambda x: x ** 2, nums))
print(squares) # [1, 4, 9, 16, 25]
📦 常见用法
# 字符串转整数
str_nums = ["1", "2", "3", "4", "5"]
int_nums = list(map(int, str_nums))
print(int_nums) # [1, 2, 3, 4, 5]
# 全部转大写
words = ["hello", "world", "python"]
upper = list(map(str.upper, words))
print(upper) # ['HELLO', 'WORLD', 'PYTHON']
# 多个列表同步处理
prices = [100, 200, 300]
discounts = [0.9, 0.8, 0.7]
final = list(map(lambda p, d: round(p * d, 2), prices, discounts))
print(final) # [90.0, 160.0, 210.0]
# 提取对象属性
users = [
{"name": "张三", "age": 25},
{"name": "李四", "age": 30},
{"name": "王五", "age": 28},
]
names = list(map(lambda u: u["name"], users))
print(names) # ['张三', '李四', '王五']
💡 map 的惰性特性:
map() 返回的是一个迭代器(iterator),不会立即计算。
用 list() 才会真正执行。数据量大时,惰性计算更省内存。
2. filter:批量筛选
filter(函数, 可迭代对象) — 只保留函数返回 True 的元素。
# 传统写法
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = []
for n in nums:
if n % 2 == 0:
evens.append(n)
print(evens) # [2, 4, 6, 8, 10]
# filter 写法
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = list(filter(lambda x: x % 2 == 0, nums))
print(evens) # [2, 4, 6, 8, 10]
📦 常见用法
# 过滤空字符串
words = ["hello", "", "world", "", "python"]
non_empty = list(filter(None, words)) # None 作为函数 = 去掉"假值"
print(non_empty) # ['hello', 'world', 'python']
# 过滤负数
nums = [3, -1, 4, -5, 9, -2, 6]
positive = list(filter(lambda x: x > 0, nums))
print(positive) # [3, 4, 9, 6]
# 过滤成年人
people = [
{"name": "张三", "age": 15},
{"name": "李四", "age": 22},
{"name": "王五", "age": 17},
{"name": "赵六", "age": 30},
]
adults = list(filter(lambda p: p["age"] >= 18, people))
names = list(map(lambda p: p["name"], adults))
print(names) # ['李四', '赵六']
# 组合使用 map + filter
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 筛选偶数,然后求平方
result = list(map(lambda x: x**2, filter(lambda x: x % 2 == 0, nums)))
print(result) # [4, 16, 36, 64, 100]
💡 filter 里的 None:
filter(None, seq) 会去掉所有"假值":0、""、None、[]、{} 等。
这是快速清理数据的利器。
3. reduce:累积计算
reduce(函数, 可迭代对象) — 把一个序列"折叠"成一个值。
from functools import reduce
# 求和:[1, 2, 3, 4, 5] → 1+2+3+4+5 = 15
nums = [1, 2, 3, 4, 5]
total = reduce(lambda a, b: a + b, nums)
print(total) # 15
# 求积:[1, 2, 3, 4, 5] → 1×2×3×4×5 = 120
product = reduce(lambda a, b: a * b, nums)
print(product) # 120
# 执行过程(以求和为例):
# 第1步: a=1, b=2 → 3
# 第2步: a=3, b=3 → 6
# 第3步: a=6, b=4 → 10
# 第4步: a=10, b=5 → 15
📦 常见用法
from functools import reduce
# 找最大值
nums = [23, 45, 12, 67, 34, 89, 56]
max_val = reduce(lambda a, b: a if a > b else b, nums)
print(max_val) # 89
# 拼接字符串
words = ["Python", "is", "awesome"]
sentence = reduce(lambda a, b: a + " " + b, words)
print(sentence) # "Python is awesome"
# 扁平化嵌套列表
nested = [[1, 2], [3, 4], [5, 6]]
flat = reduce(lambda a, b: a + b, nested)
print(flat) # [1, 2, 3, 4, 5, 6]
# 带初始值
nums = [1, 2, 3, 4, 5]
total = reduce(lambda a, b: a + b, nums, 100) # 从 100 开始加
print(total) # 115
💡 reduce 的初始值:
reduce(func, seq, init) — 第三个参数是初始值。
空列表时必须给初始值,否则报错。建议养成给初始值的习惯。
4. zip:并行遍历
zip(序列1, 序列2, ...) — 把多个序列"拉链式"合并,按位置配对。
# 基本用法
names = ["张三", "李四", "王五"]
scores = [85, 92, 78]
# 配对
paired = list(zip(names, scores))
print(paired) # [('张三', 85), ('李四', 92), ('王五', 78)]
# 并行遍历
for name, score in zip(names, scores):
print(f"{name}: {score}分")
# 多个序列
names = ["张三", "李四", "王五"]
math = [85, 92, 78]
english = [90, 88, 95]
for name, m, e in zip(names, math, english):
print(f"{name}: 数学{m}, 英语{e}")
📦 常见用法
# 创建字典
keys = ["name", "age", "city"]
values = ["张三", 25, "北京"]
person = dict(zip(keys, values))
print(person) # {'name': '张三', 'age': 25, 'city': '北京'}
# 同时遍历两个列表,找最大值
a = [1, 5, 3, 9, 7]
b = [2, 4, 6, 8, 0]
maxs = [max(x, y) for x, y in zip(a, b)]
print(maxs) # [2, 5, 6, 9, 7]
# 转置矩阵(行列互换)
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
transposed = list(zip(*matrix))
print(transposed) # [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
# zip 不等长:以最短的为准
a = [1, 2, 3]
b = [10, 20]
print(list(zip(a, b))) # [(1, 10), (2, 20)] — 3 被丢弃
# 等长填充:zip_longest
from itertools import zip_longest
print(list(zip_longest(a, b, fillvalue=0))) # [(1, 10), (2, 20), (3, 0)]
5. enumerate:带编号遍历
enumerate(序列) — 遍历时同时拿到索引和值,不用手写 range(len())。
# ❌ 传统写法(丑)
fruits = ["苹果", "香蕉", "橘子"]
for i in range(len(fruits)):
print(f"{i}: {fruits[i]}")
# ✅ enumerate 写法(优雅)
for i, fruit in enumerate(fruits):
print(f"{i}: {fruit}")
# 自定义起始编号
for i, fruit in enumerate(fruits, start=1):
print(f"第{i}个: {fruit}")
📦 常见用法
# 找元素的索引
words = ["apple", "banana", "cherry", "banana"]
for i, word in enumerate(words):
if word == "banana":
print(f"找到 banana 在索引 {i}")
break
# 带编号的排行榜
scores = {"张三": 85, "李四": 92, "王五": 78, "赵六": 95}
ranked = sorted(scores.items(), key=lambda x: -x[1])
for rank, (name, score) in enumerate(ranked, start=1):
medal = "🥇" if rank == 1 else "🥈" if rank == 2 else "🥉" if rank == 3 else f"#{rank}"
print(f" {medal} {name}: {score}分")
# 给列表加编号
items = ["Python", "Java", "C++"]
numbered = list(enumerate(items, start=1))
print(numbered) # [(1, 'Python'), (2, 'Java'), (3, 'C++')]
💡 enumerate vs range(len()):
enumerate 更 Pythonic,更高效,更安全。
面试中写 range(len()) 会被扣印象分 😅
6. sorted:排序(进阶)
sorted() 的 key 参数是排序的灵魂——告诉 Python 按什么排。
# 基本排序
nums = [3, 1, 4, 1, 5, 9, 2, 6]
print(sorted(nums)) # 升序
print(sorted(nums, reverse=True)) # 降序
# 按字符串长度排序
words = ["banana", "apple", "hi", "cherry", "a"]
print(sorted(words, key=len)) # ['a', 'hi', 'apple', 'banana', 'cherry']
# 忽略大小写排序
words = ["Banana", "apple", "Cherry", "date"]
print(sorted(words, key=str.lower)) # ['apple', 'Banana', 'Cherry', 'date']
🎯 多级排序
# 先按年龄,再按姓名
people = [
("张三", 25), ("李四", 22), ("王五", 25), ("赵六", 22)
]
result = sorted(people, key=lambda p: (p[1], p[0]))
print(result)
# [('李四', 22), ('赵六', 22), ('张三', 25), ('王五', 25)]
# 复杂排序:成绩降序,同分按姓名升序
students = [
("张三", 85), ("李四", 92), ("王五", 85), ("赵六", 92)
]
result = sorted(students, key=lambda s: (-s[1], s[0]))
print(result)
# [('李四', 92), ('赵六', 92), ('张三', 85), ('王五', 85)]
🎯 operator 模块(更优雅的 key)
from operator import itemgetter, attrgetter
# itemgetter: 按字典的某个键排序
users = [
{"name": "张三", "age": 25, "score": 85},
{"name": "李四", "age": 22, "score": 92},
{"name": "王五", "age": 28, "score": 78},
]
by_score = sorted(users, key=itemgetter("score"), reverse=True)
print([u["name"] for u in by_score]) # ['李四', '张三', '王五']
# 多字段
by_age_score = sorted(users, key=itemgetter("age", "score"))
print([(u["name"], u["age"]) for u in by_age_score])
7. reversed:反转
# 反转列表
nums = [1, 2, 3, 4, 5]
print(list(reversed(nums))) # [5, 4, 3, 2, 1]
# 反转字符串
s = "hello"
print("".join(reversed(s))) # "olleh"
# 倒序遍历
for i in reversed(range(1, 6)):
print(i, end=" ") # 5 4 3 2 1
# reversed vs [::-1]
# reversed() 返回迭代器(惰性,省内存)
# [::-1] 创建新列表(立即,占内存)
nums = [1, 2, 3, 4, 5]
print(nums[::-1]) # [5, 4, 3, 2, 1](新列表)
print(list(reversed(nums))) # [5, 4, 3, 2, 1](迭代器转列表)
8. any / all:逻辑判断
✅ any:有一个为 True 就行
# any(iterable) — 只要有一个元素为 True,就返回 True
print(any([0, 0, 1, 0])) # True(有一个 1)
print(any([0, 0, 0, 0])) # False(全是 0)
print(any([False, None, "", 42])) # True(42 是真值)
# 实用场景
nums = [1, 3, 5, 7, 8]
print(any(n % 2 == 0 for n in nums)) # True(有偶数)
scores = [45, 60, 78, 30]
print(any(s >= 60 for s in scores)) # True(有人及格)
# 检查是否有任何负数
data = [10, -3, 25, 0, -1]
print(any(x < 0 for x in data)) # True
✅ all:全部为 True 才行
# all(iterable) — 所有元素都为 True,才返回 True
print(all([1, 2, 3, 4])) # True(全非零)
print(all([1, 0, 3, 4])) # False(有一个 0)
print(all([True, True, True])) # True
# 实用场景
nums = [2, 4, 6, 8, 10]
print(all(n % 2 == 0 for n in nums)) # True(全是偶数)
scores = [60, 78, 92, 85]
print(all(s >= 60 for s in scores)) # True(全部及格)
# 检查是否所有用户都有邮箱
users = [
{"name": "张三", "email": "z@test.com"},
{"name": "李四", "email": "l@test.com"},
{"name": "王五"}, # 没有邮箱
]
print(all("email" in u for u in users)) # False
💡 any/all 的短路特性:
any 遇到第一个 True 就停,all 遇到第一个 False 就停。
配合生成器表达式,效率极高!
9. 实战练习
🎯 练习 1:成绩分析器(综合运用)
from functools import reduce
# 原始数据
students = [
{"name": "张三", "math": 85, "english": 90, "python": 88},
{"name": "李四", "math": 92, "english": 78, "python": 95},
{"name": "王五", "math": 78, "english": 85, "python": 82},
{"name": "赵六", "math": 95, "english": 92, "python": 90},
{"name": "孙七", "math": 60, "english": 65, "python": 70},
]
# 1. 计算每人平均分(map)
subjects = ["math", "english", "python"]
for s in students:
s["avg"] = round(sum(s[sub] for sub in subjects) / len(subjects), 1)
# 2. 筛选平均分 >= 80 的学生(filter)
good_students = list(filter(lambda s: s["avg"] >= 80, students))
# 3. 按平均分降序排序(sorted)
ranked = sorted(good_students, key=lambda s: -s["avg"])
# 4. 生成排行榜(enumerate)
print("🏆 优秀学生排行榜(平均分 ≥ 80)")
print("-" * 45)
for rank, s in enumerate(ranked, start=1):
medal = "🥇🥈🥉"[rank-1] if rank <= 3 else f"#{rank}"
print(f" {medal} {s['name']:4s} | 均分 {s['avg']:5.1f} | 数学{s['math']} 英语{s['english']} Python{s['python']}")
# 5. 全班各科平均分(zip + map)
class_avg = {}
for sub in subjects:
scores = list(map(lambda s: s[sub], students))
class_avg[sub] = round(sum(scores) / len(scores), 1)
print(f"\n📊 全班各科平均分:")
for sub, avg in class_avg.items():
print(f" {sub:8s}: {avg}")
# 6. 检查是否有满分(any)
print(f"\n🔍 有任何科目满分吗? {any(s[sub] == 100 for s in students for sub in subjects)}")
# 7. 检查是否全部及格(all)
print(f"✅ 全部及格吗? {all(s[sub] >= 60 for s in students for sub in subjects)}")
🎯 练习 2:数据清洗管道
from functools import reduce
# 模拟原始数据(脏数据)
raw_data = [
" 张三, 25, 85.5 ",
"李四, , 92.0",
"王五, 28, N/A",
" 赵六, 30, 78.5 ",
", , ",
"孙七, 22, 88.0",
]
# 清洗管道:每一步都是一个函数
def strip_lines(lines):
"""去除首尾空白"""
return list(map(str.strip, lines))
def remove_empty(lines):
"""去除空行"""
return list(filter(lambda l: l and not all(c.strip() == '' for c in l.split(',')), lines))
def parse_records(lines):
"""解析为字典"""
records = []
for line in lines:
parts = [p.strip() for p in line.split(",")]
if len(parts) == 3 and parts[0] and parts[1] and parts[2] not in ("N/A", ""):
try:
records.append({
"name": parts[0],
"age": int(parts[1]),
"score": float(parts[2]),
})
except ValueError:
pass
return records
def filter_valid(records):
"""筛选有效记录"""
return list(filter(lambda r: r["age"] > 0 and r["score"] > 0, records))
def sort_by_score(records):
"""按分数排序"""
return sorted(records, key=lambda r: -r["score"])
# 管道:raw_data → strip → remove_empty → parse → filter → sort
pipeline = [strip_lines, remove_empty, parse_records, filter_valid, sort_by_score]
clean_data = reduce(lambda data, func: func(data), pipeline, raw_data)
print("📋 清洗后的数据:")
for r in clean_data:
print(f" {r['name']} | {r['age']}岁 | {r['score']}分")
🎯 练习 3:矩阵运算(zip 实战)
# 矩阵加法
def matrix_add(A, B):
return [[a + b for a, b in zip(row_a, row_b)] for row_a, row_b in zip(A, B)]
# 矩阵转置
def matrix_transpose(M):
return [list(row) for row in zip(*M)]
# 矩阵乘法
def matrix_multiply(A, B):
B_T = matrix_transpose(B)
return [
[sum(a * b for a, b in zip(row_a, col_b)) for col_b in B_T]
for row_a in A
]
# 测试
A = [[1, 2], [3, 4]]
B = [[5, 6], [7, 8]]
print("A + B =")
for row in matrix_add(A, B):
print(f" {row}")
print("\nA 的转置 =")
for row in matrix_transpose(A):
print(f" {row}")
print("\nA × B =")
for row in matrix_multiply(A, B):
print(f" {row}")
10. 今日小结
| | |
|---|
map(func, seq) | | |
filter(func, seq) | | |
reduce(func, seq) | | |
zip(*seqs) | | |
enumerate(seq) | | |
sorted(seq, key=) | | |
reversed(seq) | | |
any(seq) | | |
all(seq) | | |
🧠 记忆口诀:
map 转换 filter 筛,reduce 累积算出来。
zip 拉链 enumerate,sorted 排序 reversed 倒。
any 一个 all 全部,函数式编程真好用。
🔮 预告: Day 16 文件操作 — open()、读写模式、with 语句。让程序和文件对话!