零基础学 Python 第11章 | 匿名函数与高阶函数
本章摘要
- 核心目标:掌握
lambda与map/filter/sorted,用简洁代码完成批量数据处理 - 核心知识点:
lambda语法、map()映射、filter()过滤、sorted()排序及key参数 - 实操产出:计算器(
lambda四则运算)+ 密码强度校验(高阶函数批量处理) - 前置基础:第10章函数进阶(参数、返回值、作用域)、第4章循环、第6章列表
一、写在前面
上一章你学了函数进阶——默认参数、可变参数 *args/**kwargs、返回值、变量作用域。现在你已经能写出结构清晰的函数了。
但你有没有遇到过这种情况:某个函数特别简单,比如"把一个数乘以 2",你只想用它一次,却还得 def 起个名字、写两行代码。这就好比你只想喝口水,却非要正式地摆好茶具——太重了。
Python 提供了一个更轻量的方案:lambda 匿名函数。一行搞定,不用起名字,用完即走。围绕着 lambda,Python 还内置了三个高阶函数——map()、filter()、sorted(),它们能把"对数据批量操作"这件事做到极致简洁。
本章就来一次性拿下这四个工具。学完之后,你处理列表数据的能力会有质的飞跃。
二、什么是高阶函数
在讲具体工具之前,先搞清楚一个概念:高阶函数。
高阶函数就是接收函数作为参数,或者返回一个函数的函数。说白了,函数不再只是被调用的"打工人",它还可以被当作"数据"传来传去。
# 你已经熟悉的普通函数调用
print(len("hello")) # len 是函数,"hello" 是数据
# 高阶函数:把函数当作参数传给另一个函数
numbers = [1, 2, 3, 4, 5]
# map 接收了一个函数(lambda)作为参数
result = list(map(lambda x: x ** 2, numbers))
print(result) # [1, 4, 9, 16, 25]
你看,map() 接收的第一个参数不是数字、不是字符串,而是一个函数。这就是高阶函数的核心特征。
Python 内置的高阶函数不止三个,但最常用的就是 map()、filter() 和 sorted()。它们三个配合 lambda,能覆盖大部分"批量处理数据"的场景。下面逐个击破。
三、lambda 匿名函数
3.1 lambda 是什么
lambda 是一行搞定的匿名函数。不需要 def,不需要 return,写完就能用。
语法非常简单:
lambda 参数: 表达式
lambda是关键字- 冒号左边是参数(可以没有,可以多个)
- 冒号右边是返回值(只能写一个表达式,不能写多行语句)
# 用 def 写一个加法函数
def add(x, y):
return x + y
# 用 lambda 写同样的加法
add_lambda = lambda x, y: x + y
print(add(3, 5)) # 8
print(add_lambda(3, 5)) # 8
两种写法效果一模一样,但 lambda 只用了一行。
3.2 lambda 的参数玩法
lambda 的参数规则和普通函数完全一样——可以有多个参数,可以有默认值:
# 无参数
greet = lambda: "你好!"
print(greet()) # 你好!
# 一个参数
square = lambda x: x ** 2
print(square(5)) # 25
# 多个参数
add = lambda x, y: x + y
print(add(3, 7)) # 10
# 带默认值
power = lambda x, n=2: x ** n
print(power(3)) # 9(默认平方)
print(power(3, 3)) # 27(指定立方)
3.3 lambda vs def:什么时候用哪个
| 对比项 | def | lambda |
|---|---|---|
| 行数 | 多行,可写复杂逻辑 | 一行,只能一个表达式 |
| 函数名 | 必须有名字 | 匿名,不需要起名 |
| 返回值 | 用 return 返回 | 表达式结果自动返回 |
| 可读性 | 逻辑清晰,适合复用 | 简洁,适合一次性使用 |
| 适用场景 | 复杂逻辑、需要复用 | 简单操作、作为参数传递 |
一句话原则: 逻辑简单(一行能写完)且只用一次,用 lambda;逻辑复杂或需要多处复用,用 def。
# ✅ 适合 lambda:简单的数学运算,作为参数传给 sorted
students = [{"name": "小明", "score": 85}, {"name": "小红", "score": 92}]
ranked = sorted(students, key=lambda s: s["score"])
# ❌ 不适合 lambda:多步骤逻辑,强行写成 lambda 会很难读
# 下面这行能运行,但没人看得懂,不如用 def
# process = lambda data: [x ** 2 for x in data if x > 0] # 能看懂但何必呢
新手建议:刚开始学,遇到拿不准的情况就用
def。lambda用熟了自然知道什么时候该用。
四、map() 映射:批量处理利器
4.1 map() 做什么
map() 的作用是:对一个可迭代对象中的每个元素,都套用同一个函数,然后返回结果。
语法:
map(函数, 可迭代对象)
比如你有一个字符串列表 ["1", "2", "3"],想把每个都转成整数,一个个写循环太麻烦,map 一行搞定:
str_nums = ["1", "2", "3", "4", "5"]
# map 把 int 函数套用到每个元素上
result = list(map(int, str_nums))
print(result) # [1, 2, 3, 4, 5]
注意:
map()返回的是map对象(迭代器),不是列表。需要用list()转换才能看到内容。这是新手最容易忽略的地方。
4.2 map() 配合 lambda
map() 最常见的用法是配合 lambda,自定义变换规则:
# 把一组摄氏温度转华氏温度:F = C * 1.8 + 32
celsius = [0, 10, 20, 30, 40]
fahrenheit = list(map(lambda c: c * 1.8 + 32, celsius))
print(fahrenheit) # [32.0, 50.0, 68.0, 86.0, 104.0]
# 把一组数字全部翻倍
numbers = [1, 2, 3, 4, 5]
doubled = list(map(lambda x: x * 2, numbers))
print(doubled) # [2, 4, 6, 8, 10]
# 把一组名字全部转大写
names = ["alice", "bob", "charlie"]
upper_names = list(map(str.upper, names))
print(upper_names) # ['ALICE', 'BOB', 'CHARLIE']
第三个例子直接传了 str.upper 方法名,省去了 lambda,更简洁。
4.3 map() 处理多个可迭代对象
map() 可以同时接收多个可迭代对象,函数的参数个数要对应:
# 两个列表对应位置相加
prices = [10, 20, 30]
quantities = [2, 3, 1]
# lambda 接收两个参数,分别来自两个列表
totals = list(map(lambda p, q: p * q, prices, quantities))
print(totals) # [20, 60, 30]
如果两个列表长度不同,
map会以最短的为准,多出来的元素自动忽略。
4.4 map() vs for 循环
同样的功能,用 for 循环也能写,但 map 更简洁:
# for 循环写法
numbers = [1, 2, 3, 4, 5]
squared = []
for n in numbers:
squared.append(n ** 2)
# map 写法(一行搞定)
squared = list(map(lambda x: x ** 2, numbers))
不是说要抛弃 for 循环,而是在"对每个元素做同样变换"的场景下,map 更直观、更省代码。
五、filter() 过滤:按条件筛选
5.1 filter() 做什么
filter() 的作用是:按照条件筛选元素,留下满足条件的,丢掉不满足的。
语法:
filter(函数, 可迭代对象)
传入的函数必须返回 True 或 False。返回 True 的元素保留,返回 False 的丢弃。
# 筛选列表中的偶数
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def is_even(n):
return n % 2 == 0
evens = list(filter(is_even, numbers))
print(evens) # [2, 4, 6, 8, 10]
5.2 filter() 配合 lambda
和 map 一样,filter 最常见的搭档也是 lambda:
# 筛选偶数(lambda 版)
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # [2, 4, 6, 8, 10]
# 筛选长度大于 3 的字符串
words = ["hi", "hello", "ok", "python", "go"]
long_words = list(filter(lambda w: len(w) > 3, words))
print(long_words) # ['hello', 'python']
# 筛选及格成绩(≥60)
scores = [45, 78, 90, 56, 61, 33, 88]
passed = list(filter(lambda s: s >= 60, scores))
print(passed) # [78, 90, 61, 88]
5.3 filter() 的返回值
和 map 一样,filter() 返回的也是迭代器对象,需要用 list() 转换:
result = filter(lambda x: x > 5, [1, 6, 3, 8, 2, 9])
print(result) # <filter object at 0x...>
print(list(result)) # [6, 8, 9]
如果筛选后没有匹配的元素,结果就是空列表
[],不会报错。
5.4 map vs filter:别搞混
| 对比 | map() | filter() |
|---|---|---|
| 作用 | 变换每个元素 | 筛选元素 |
| 函数返回值 | 任意值(变换后的结果) | True / False |
| 元素数量 | 输入几个,输出几个 | 输出 ≤ 输入(被过滤掉的不要) |
| 类比 | 给每个人发一顶帽子 | 只让戴帽子的人进去 |
一句话区分:map 是"全都变",filter 是"挑着留"。
六、sorted() 排序:自定义排序规则
6.1 sorted() 基本用法
sorted() 的作用是:对可迭代对象排序,返回一个新的排好序的列表。
# 数字排序
scores = [78, 92, 56, 88, 65]
print(sorted(scores)) # [56, 65, 78, 88, 92] 升序
print(sorted(scores, reverse=True)) # [92, 88, 78, 65, 56] 降序
# 字符串排序(按字母顺序)
names = ["charlie", "alice", "bob"]
print(sorted(names)) # ['alice', 'bob', 'charlie']
sorted()返回新列表,不修改原数据。如果想原地排序,用列表.sort(),但这会改变原列表。
6.2 key 参数:自定义排序规则
这是 sorted() 最强大的功能。key 参数接收一个函数,函数作用于每个元素,排序时按函数的返回值来比较,但最终输出的还是原始元素。
# 按绝对值排序(不看正负,只看大小)
numbers = [-5, 3, -1, 8, -2]
print(sorted(numbers, key=abs))
# [−1, −2, 3, −5, 8] → 按 |−1|, |−2|, |3|, |−5|, |8| 排列
# 按字符串长度排序
words = ["banana", "apple", "kiwi", "strawberry"]
print(sorted(words, key=len))
# ['kiwi', 'apple', 'banana', 'strawberry']
6.3 sorted() 配合 lambda
key 参数最常搭配 lambda 使用,灵活定义排序依据:
# 按成绩从高到低排序学生
students = [
{"name": "小明", "score": 85},
{"name": "小红", "score": 92},
{"name": "小刚", "score": 78},
{"name": "小美", "score": 96},
]
# lambda 取出每个学生的 score 作为排序依据
ranked = sorted(students, key=lambda s: s["score"], reverse=True)
for s in ranked:
print(f"{s['name']}:{s['score']}分")
运行结果:
小美:96分
小红:92分
小明:85分
小刚:78分
没有
key参数,sorted()不知道怎么比较字典,会直接报错(后面报错速查表会讲)。
6.4 多条件排序
key 函数可以返回元组,sorted() 会按元组的元素依次比较——先比第一个,相同再比第二个:
# 先按成绩降序,成绩相同再按姓名升序
students = [
{"name": "小明", "score": 85},
{"name": "小刚", "score": 85},
{"name": "小红", "score": 92},
{"name": "小美", "score": 96},
]
# 成绩取负数实现降序,姓名正序
ranked = sorted(students, key=lambda s: (-s["score"], s["name"]))
for s in ranked:
print(f"{s['name']}:{s['score']}分")
运行结果:
小美:96分
小红:92分
小刚:85分
小明:85分
小刚和小明都是 85 分,按姓名拼音排,"小刚"在"小明"前面。多条件排序在实际开发中非常常用。
七、lambda + 高阶函数组合使用
7.1 map + filter 链式调用
实际开发中,经常需要"先筛选,再变换"。filter 和 map 可以串联使用:
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 第一步:filter 筛选偶数
evens = list(filter(lambda x: x % 2 == 0, numbers))
# 第二步:map 把偶数平方
squares = list(map(lambda x: x ** 2, evens))
print(squares) # [4, 16, 36, 64, 100]
也可以一步到位,把 filter 的结果直接传给 map:
# 嵌套写法:一步搞定
result = list(map(lambda x: x ** 2, filter(lambda x: x % 2 == 0, numbers)))
print(result) # [4, 16, 36, 64, 100]
嵌套写法虽然简洁,但可读性不如分步写。建议新手先写分步版,熟练后再用嵌套。
7.2 四个工具速查表
| 工具 | 作用 | 语法 | 返回值 | 是否改原数据 |
|---|---|---|---|---|
lambda | 创建匿名函数 | lambda 参数: 表达式 | 函数对象 | 不涉及 |
map() | 对每个元素变换 | map(函数, 可迭代对象) | map 对象 | 否 |
filter() | 按条件筛选 | filter(函数, 可迭代对象) | filter 对象 | 否 |
sorted() | 排序 | sorted(可迭代对象, key=函数, reverse=bool) | 新列表 | 否 |
四个工具都不修改原始数据,安全放心。
八、Scratch 积木对照
本章的 lambda 匿名函数、map/filter/sorted 高阶函数属于 Python 进阶特性,Scratch 中没有对应的积木。
原因很简单:Scratch 的积木是图形化、命名的,每个积木块都有明确的名字和形状。而 lambda 的核心就是"匿名"——连名字都不要,Scratch 的设计模型里根本没有这个概念。高阶函数"把函数当参数传给另一个函数"更是 Scratch 编程模型中不存在的操作。
这些知识点没有捷径,多写几遍代码就熟了。好在语法都很简洁,下面综合实战会一次性把它们串起来用。
九、综合实战:封装计算器 + 密码校验工具
本案例为第三部分(函数篇)毕业案例,综合运用第 9-11 章所学:函数定义、参数传递、返回值、作用域、
lambda、map/filter/sorted。
案例说明
我们要写两个工具:
- 计算器函数:支持加减乘除,用
lambda实现运算逻辑 - 密码强度校验函数:判断密码强度(长度、是否含数字、是否含特殊字符)
然后用 map/filter/sorted 对一批密码做批量校验和排名。
完整源码
# ========================================
# 综合实战:封装计算器 + 密码校验工具
# 第三部分(函数篇)毕业案例
# ========================================
# ---------- Part 1:计算器(lambda 实现四则运算) ----------
def calculator(a, b, operator):
"""计算器函数:用 lambda 实现四则运算"""
# 用字典存储运算符到 lambda 的映射
operations = {
"+": lambda x, y: x + y,
"-": lambda x, y: x - y,
"*": lambda x, y: x * y,
"/": lambda x, y: x / y if y != 0 else "错误:除数不能为0",
}
if operator not in operations:
return f"不支持的操作符:{operator}"
return operations[operator](a, b)
# 测试计算器
print("===== 计算器测试 =====")
print(f"10 + 3 = {calculator(10, 3, '+')}")
print(f"10 - 3 = {calculator(10, 3, '-')}")
print(f"10 * 3 = {calculator(10, 3, '*')}")
print(f"10 / 3 = {calculator(10, 3, '/')}")
print(f"10 / 0 = {calculator(10, 0, '/')}")
print(f"10 % 3 = {calculator(10, 3, '%')}")
# ---------- Part 2:密码强度校验 ----------
def has_digit(text):
"""检查字符串中是否包含数字"""
for char in text:
if char.isdigit():
return True
return False
def has_special(text):
"""检查字符串中是否包含特殊字符"""
specials = "!@#$%^&*()-_=+"
for char in text:
if char in specials:
return True
return False
def check_password(password):
"""检查密码强度,返回包含详细信息的字典"""
# 用 lambda 和已有函数定义校验规则
rules = [
("长度≥8位", lambda p: len(p) >= 8),
("包含数字", has_digit),
("包含特殊字符", has_special),
]
passed = []
for rule_name, check_func in rules:
if check_func(password):
passed.append(rule_name)
score = len(passed)
if score == 3:
strength = "强"
elif score == 2:
strength = "中"
else:
strength = "弱"
return {
"password": password,
"strength": strength,
"score": score,
"passed": passed,
}
# 测试单个密码
print("\n===== 密码校验测试 =====")
result = check_password("P@ssw0rd!")
print(f"密码:{result['password']}")
print(f"强度:{result['strength']}({result['score']}/3)")
print(f"通过:{result['passed']}")
# ---------- Part 3:批量处理(map + filter + sorted) ----------
print("\n===== 批量密码校验 =====")
# 一批待检查的密码
passwords = ["123", "abc12345", "P@ssw0rd!", "hello", "MyP@ss2024"]
# map():批量校验所有密码
results = list(map(check_password, passwords))
# filter():筛选弱密码(score ≤ 1)
weak_passwords = list(filter(lambda r: r["score"] <= 1, results))
# sorted():按强度分数从低到高排序
ranked = sorted(results, key=lambda r: r["score"])
print("密码强度排名(从弱到强):")
for r in ranked:
print(f" {r['password']:<12} 强度:{r['strength']} 通过:{r['passed']}")
print(f"\n⚠️ 检测到 {len(weak_passwords)} 个弱密码,建议立即修改!")
for w in weak_passwords:
print(f" ❌ {w['password']}")
运行结果:
===== 计算器测试 =====
10 + 3 = 13
10 - 3 = 7
10 * 3 = 30
10 / 3 = 3.3333333333333335
10 / 0 = 错误:除数不能为0
10 % 3 = 不支持的操作符:%
===== 密码校验测试 =====
密码:P@ssw0rd!
强度:强(3/3)
通过:['长度≥8位', '包含数字', '包含特殊字符']
===== 批量密码校验 =====
密码强度排名(从弱到强):
123 强度:弱 通过:['包含数字']
hello 强度:弱 通过:[]
abc12345 强度:中 通过:['长度≥8位', '包含数字']
P@ssw0rd! 强度:强 通过:['长度≥8位', '包含数字', '包含特殊字符']
MyP@ss2024 强度:强 通过:['长度≥8位', '包含数字', '包含特殊字符']
⚠️ 检测到 2 个弱密码,建议立即修改!
❌ 123
❌ hello
分步拆解
Part 1 — 计算器函数 calculator
- 字典 +
lambda存储运算逻辑:用字典把运算符映射到对应的lambda函数。"+"对应lambda x, y: x + y,调用时直接operations[operator](a, b)就能执行运算。这比写一堆if-else优雅得多。 - 除零保护:除法
lambda中用条件表达式x / y if y != 0 else "错误:除数不能为0",避免程序崩溃。 - 非法操作符处理:先检查
operator not in operations,不支持的运算符返回提示信息,而不是报错。
Part 2 — 密码强度校验 check_password
- 辅助函数
has_digit/has_special:这两个函数逻辑稍复杂(需要遍历字符),用def定义更清晰。它们的参数和返回值都很明确——接收字符串,返回布尔值。 - rules 列表存储校验规则:每条规则是一个元组
(规则名, 检查函数)。长度检查用lambda(简单一行),数字和特殊字符检查用已有的def函数。这体现了"什么时候用lambda,什么时候用def"的实际判断。 - 遍历规则逐条检查:
for循环遍历rules,调用每个检查函数,通过的规则记入passed列表。check_func就是被当作参数传递的函数——这就是高阶函数的思路。 - 返回字典:返回包含密码、强度、分数、通过项的字典。调用者可以按需取用任意字段。
Part 3 — 批量处理(高阶函数三连)
- map() 批量校验:
map(check_password, passwords)把check_password函数套用到每个密码上,一行代码完成五个密码的校验。 - filter() 筛选弱密码:
filter(lambda r: r["score"] <= 1, results)从结果中筛出弱密码。这里lambda作为筛选条件,简洁明了。 - sorted() 排序:
sorted(results, key=lambda r: r["score"])按强度分数排序,weakest排最前面,一目了然。
整个案例从函数定义到批量处理,串联了第 9-11 章的全部核心知识点。你可以把密码列表换成自己的密码试试看强度如何。
十、常见报错速查表
| 报错信息 | 出现场景 | 原因 | 解决方法 |
|---|---|---|---|
TypeError: <lambda>() missing 1 required positional argument | 调用 lambda | 传参个数不够,如 lambda x, y: x+y 只传了一个参数 | 确保参数个数与 lambda 定义一致 |
TypeError: 'map' object is not subscriptable | 访问 map 结果 | 直接用 map_result[0] 取值 | 先 list(map_result) 转成列表再索引 |
TypeError: 'filter' object is not subscriptable | 访问 filter 结果 | 直接用 filter_result[0] 取值 | 先 list(filter_result) 转成列表再索引 |
TypeError: 'NoneType' object is not callable | map/filter 传参 | 第一个参数传了 None 或忘传函数 | 确保第一个参数是可调用的函数 |
TypeError: '<' not supported between instances of 'dict' and 'dict' | sorted 排序字典 | 没加 key 参数,sorted 不知道怎么比较字典 | 添加 key=lambda s: s["字段名"] |
ZeroDivisionError: division by zero | lambda 除法 | 除法运算中除数为 0 | 加条件判断:x / y if y != 0 else "错误" |
十一、本章小结
本章你掌握了 Python 匿名函数与三大高阶函数:
lambda匿名函数——lambda 参数: 表达式,一行搞定简单函数,适合一次性使用和作为参数传递map()映射——map(函数, 可迭代对象),对每个元素做变换,返回 map 对象需list()转换filter()过滤——filter(函数, 可迭代对象),按条件筛选元素,函数返回 True 保留、False 丢弃sorted()排序——sorted(可迭代对象, key=函数, reverse=bool),key 参数自定义排序规则,可配合 lambda 灵活排序- 组合使用——
map+filter链式调用,sorted多条件排序,四个工具搭配处理数据事半功倍
这四个工具是 Python 函数式编程的入门钥匙。当你发现自己写了大量"遍历 + 变换 + 筛选"的 for 循环时,想想能不能用 map/filter/sorted 替代,代码会简洁很多。
往期回顾:
零基础学 Python 第1章 | 全平台装环境 + VS Code 配置 + 第一个 Hello World
零基础学 Python 第2章 | print 输出 + 变量赋值 + input 交互输入
零基础学 Python 第3章 | 条件判断:if / elif / else
零基础学 Python 第 4 章 | for 循环与 range():让程序帮你重复干活
零基础学 Python 第 5 章 | while 循环与循环嵌套:让程序看着条件自己干活
零基础学 Python 第 6 章 | 元组与集合:不可变数据与自动去重
零基础学 Python 第7章 | 字典 dict:键值对存储
零基础学 Python 第9章 | 函数基础:def、参数与返回值
零基础学 Python 第10章 | 函数进阶:参数灵活用法与作用域
下一章预告:到目前为止,你的程序运行完数据就消失了——关掉终端,所有变量、列表、字典都不见了。下一章学习文件读写,让数据真正"存"下来,程序重启还能读回来。
文末资源区
| 资源 | 说明 |
|---|---|
Python 官方文档 - lambda | https://docs.python.org/3/tutorial/controlflow.html#lambda-expressions |
Python 官方文档 - map() | https://docs.python.org/3/library/functions.html#map |
Python 官方文档 - filter() | https://docs.python.org/3/library/functions.html#filter |
Python 官方文档 - sorted() | https://docs.python.org/3/library/functions.html#sorted |
| 本章完整源码 | chapter11_all.py(关注梅雅达编程笔记,评论领资料) |
每节课都配套课后练习题与完整源码。代码报错、看不懂知识点,可在评论区粘贴问题,逐一解答!
欢迎关注梅雅达编程笔记,持续分享Python零基础学习内容
更多推荐




所有评论(0)