Python 中 lambda 和 def 有什么区别?
简化版
lambda 用来创建匿名小函数,只能写单个表达式;def 用来定义普通函数,可以包含多条语句、文档字符串、注解和复杂逻辑。面试中要强调:lambda 适合短小回调,不适合承载复杂业务逻辑。
详细版
示例:
square = lambda x: x * x
等价于:
def square(x):
return x * x
主要区别:
| 对比点 | lambda | def |
|---|---|---|
| 名称 | 通常匿名,也可赋给变量 | 有函数名 |
| 函数体 | 只能是一个表达式 | 可以是多条语句 |
| 返回值 | 表达式结果自动返回 | 使用 return 返回 |
| 可读性 | 适合短逻辑 | 适合复杂逻辑 |
| 文档和注解 | 不适合写完整说明 | 支持文档字符串和注解 |
常见使用:
users = [{"name": "Tom", "age": 18}, {"name": "Jerry", "age": 16}]
users.sort(key=lambda user: user["age"])
如果逻辑变复杂,就应该改成 def:
def user_sort_key(user):
return user["age"]
完整版教学
一、lambda 是表达式,不是语句块
lambda 的语法:
lambda 参数列表: 表达式
例如:
add = lambda a, b: a + b
冒号后面必须是表达式,表达式会自动作为返回值。
你不能在 lambda 里写多条语句:
# 错误示意
lambda x: y = x + 1
也不适合写复杂分支、循环、异常处理。虽然可以写条件表达式:
label = lambda score: "pass" if score >= 60 else "fail"
但如果逻辑继续变复杂,可读性会迅速变差。
二、def 更适合表达业务含义
def 定义的函数可以有函数名、文档字符串、类型注解和多条语句:
def normalize_name(name: str) -> str:
"""去掉首尾空格,并统一为小写。"""
return name.strip().lower()
函数名本身就是文档。看到 normalize_name,读者能大致知道它做什么;看到一长串 lambda,读者需要解析表达式才能理解。
工程代码里,可读性通常比少写几行更重要。
三、lambda 常见使用场景
lambda 适合传给高阶函数,作为一次性的小逻辑。
排序:
students = [
{"name": "Tom", "score": 80},
{"name": "Jerry", "score": 95},
]
students.sort(key=lambda s: s["score"])
映射:
nums = [1, 2, 3]
result = list(map(lambda x: x * 2, nums))
过滤:
evens = list(filter(lambda x: x % 2 == 0, nums))
不过在 Python 中,列表推导式通常比 map + lambda 更直观:
result = [x * 2 for x in nums]
evens = [x for x in nums if x % 2 == 0]
四、lambda 和闭包的关系
lambda 也会遵守作用域和闭包规则。
def make_adder(n):
return lambda x: x + n
add_10 = make_adder(10)
print(add_10(5)) # 15
它也会遇到延迟绑定问题:
funcs = [lambda: i for i in range(3)]
print([f() for f in funcs]) # [2, 2, 2]
修复:
funcs = [lambda i=i: i for i in range(3)]
所以 lambda 不是特殊作用域,它只是创建函数对象的简短语法。
五、不要为了炫技滥用 lambda
不推荐写:
process = lambda users: list(map(lambda u: u["name"].strip().lower(), filter(lambda u: u["active"], users)))
更清晰的写法:
def active_user_names(users):
names = []
for user in users:
if user["active"]:
names.append(user["name"].strip().lower())
return names
代码给机器执行,也给人维护。lambda 用得好是简洁,用过头就是阅读障碍。
六、常见误区与追问
记忆钩子:
lambda解决的是“把一个短表达式当函数传出去”,不是解决“少写def”。一旦需要命名意图、调试定位或多步逻辑,就该回到def。
- 误区:lambda 比 def 性能更高。 二者都会创建函数对象,主要差异在语法表达能力和可读性,不应把
lambda当作性能优化手段。 - 误区:lambda 不能有参数默认值。
lambda x, step=2: x + step是合法的;循环闭包里也常用lambda i=i: i固定当前循环值。 - 追问:lambda 为什么不能写赋值语句和 for 循环语句? Python 语法规定
lambda冒号后必须是单个表达式;赋值、for、try这类语句块需要用def承载。 - 追问:
lambda赋给变量后还是匿名函数吗? 函数对象本身的__name__仍通常是'<lambda>',变量名只是外部绑定;调试日志里看到<lambda>往往不如具名函数清楚。 - 误区:map/filter + lambda 一定更 Pythonic。 在很多场景下,列表推导式或生成器表达式更直接,例如
[x * 2 for x in nums]比list(map(lambda x: x * 2, nums))更容易读。 - 追问:lambda 和闭包有什么关系?
lambda和def一样遵守 LEGB 与延迟绑定规则;循环里创建 lambda 时,如果引用循环变量,调用时读到的可能是循环结束后的最终值。
七、加强记忆
lambda 是“单表达式匿名函数”,def 是“完整函数定义”。短小、一次性的 key 函数或回调用 lambda 很合适;只要逻辑需要命名、注释、分支、异常处理或复用,就果断用 def。面试时别只说语法差异,要补一句可读性和维护性取舍。