本文目录导读:
我来从源码角度分析Python lambda表达式的本质。
编译阶段的处理
字节码层面对比
先看一个简单的例子:
# lambda表达式
f1 = lambda x: x + 1
# 普通函数
def f2(x):
return x + 1
编译后的字节码对比:
import dis dis.dis(f1) # RESUME 0 # LOAD_FAST 0 (x) # LOAD_CONST 1 (1) # BINARY_OP 0 (+) # RETURN_VALUE dis.dis(f2) # RESUME 0 # LOAD_FAST 0 (x) # LOAD_CONST 1 (1) # BINARY_OP 0 (+) # RETURN_VALUE
完全相同的字节码! 这说明在底层实现上,lambda和普通函数没有区别。
编译器的处理
查看CPython源码中的编译过程:
// Python/compile.c(简化版)
static compiler_scope_t
compiler_lambda(struct compiler *c, expr_ty e)
{
// 创建一个新的代码对象
// 和普通函数的处理几乎一样
// 1. 设置函数名(永远是"<lambda>")
identifier name = e->v.Lambda.name;
if (name == NULL) {
name = "<lambda>";
}
// 2. 编译函数体
// 和编译普通函数体使用相同的函数
return compiler_function(c, e, name, 1); // 最后一个参数标记这是lambda
}
关键代码位置在 Python/compile.c 的 compiler_lambda 函数。
语法解析阶段
AST(抽象语法树)层面
import ast # 解析lambda表达式 code = "lambda x: x + 1" tree = ast.parse(code) print(ast.dump(tree, indent=2)) # Module( # body=[ # Expr( # value=Lambda( # args=arguments( # posonlyargs=[], args=[arg(arg='x')], # kwonlyargs=[], kw_defaults=[], defaults=[]), # body=BinOp( # left=Name(id='x', ctx=Load()), # op=Add(), # right=Constant(value=1))))]) # 解析普通函数 code2 = "def f(x): return x + 1" tree2 = ast.parse(code2) print(ast.dump(tree2, indent=2)) # Module( # body=[ # FunctionDef( # name='f', # args=arguments(...), # 参数部分结构相同 # body=[ # Return( # value=BinOp( # left=Name(id='x', ctx=Load()), # op=Add(), # right=Constant(value=1)))])])
可以看到,lambda生成的是 Lambda 节点,普通函数生成的是 FunctionDef 节点,但内部的表达式结构完全一致。
运行时创建过程
内置函数调用
# lambda本质上相当于调用内置函数 # 可以理解为: f1 = lambda x: x + 1 # 等价于: f2 = (lambda).__new__(lambda) # 实际上是通过 builtins.__build_class__ 创建 # 从源码角度看,lambda表达式会被转换为类似这样的操作: # MAKE_FUNCTION 字节码指令
关键区别分析
lambda的限制
# lambda不能包含语句 lambda x: print(x) # 语法错误!lambda只能包含表达式 # 但是底层实现上,如果添加return语句: lambda x: return x + 1 # SyntaxError! # 这是因为lambda的body在语法解析时被限制为expression,而不是statement
在CPython源码中的处理
// Grammar/python.gram(简化)
// lambda表达式的语法规则
lambda_expr:
|'lambda' [lambda_params] ':' expression
// 普通函数的语法规则
func_def:
|'def' NAME '(' [params] ')' '->' expression ':' suite
// 关键:lambda的body必须是expression(表达式)
// 而普通函数可以使用suite(语句序列)
内部实现的验证
# 验证lambda和普通函数的内部结构
import types
f1 = lambda x: x + 1
def f2(x):
return x + 1
# 它们的类型相同
print(type(f1) is type(f2)) # True - 都是function类型
# 内部属性相同
print(f1.__code__.co_code == f2.__code__.co_code) # True - 字节码相同
print(f1.__code__.co_consts == f2.__code__.co_consts) # True - 常量相同
# 唯一的区别
print(f1.__name__) # '<lambda>'
print(f2.__name__) # 'f2'
print(f1.__qualname__) # '<lambda>'
lambda表达式本质上是语法糖,证据如下:
- 编译结果相同:生成相同的字节码
- 运行时类型相同:都是
function类型 - 内部结构相同:有相同的属性集合
- 内存表示相同:都是
PyFunctionObject结构
lambda只是提供了一种简洁的语法形式来创建简单的匿名函数,编译器在语法解析阶段会将其转换为普通函数的形式,然后在编译阶段使用相同的代码生成路径。
唯一真实的区别是:
- lambda的name固定为`"
- lambda的body只能是表达式,不能包含语句
这证明了lambda本质上就是语法糖 - 它提供了一种更简洁的写法,但底层实现完全相同。