第 28 章 Python 底层探秘
28.1 CPython 字节码与 dis 模块
Python 源码先编译为字节码,再由虚拟机执行:
import dis
def add(a, b):
return a + b
dis.dis(add)
# 2 0 LOAD_FAST 0 (a)
# 2 LOAD_FAST 1 (b)
# 4 BINARY_ADD
# 6 RETURN_VALUE
查看字节码
# 编译为代码对象
code = compile("x = 1 + 2", "<string>", "exec")
print(code.co_code) # 原始字节码(bytes)
print(code.co_consts) # 常量表: (1, 2, None)
print(code.co_names) # 名称表: ('x',)
# 反汇编
dis.dis(code)
# 1 0 LOAD_CONST 0 (1)
# 2 LOAD_CONST 1 (2)
# 4 BINARY_ADD
# 6 STORE_NAME 0 (x)
# 8 LOAD_CONST 2 (None)
# 10 RETURN_VALUE
# 函数的代码对象
def example():
x = 10
y = 20
return x + y
code = example.__code__
print(code.co_varnames) # ('x', 'y')
print(code.co_consts) # (None, 10, 20)
print(code.co_stacksize) # 栈深度
理解性能差异
import dis
# 局部变量用 LOAD_FAST(快)
def local_var():
x = 10
return x
dis.dis(local_var)
# LOAD_CONST → STORE_FAST → LOAD_FAST → RETURN_VALUE
# 全局变量用 LOAD_GLOBAL(慢)
x = 10
def global_var():
return x
dis.dis(global_var)
# LOAD_GLOBAL → RETURN_VALUE
# 属性访问用 LOAD_ATTR(更慢)
class Obj:
x = 10
def attr_access():
return Obj.x
dis.dis(attr_access)
# LOAD_GLOBAL → LOAD_ATTR → RETURN_VALUE
28.2 PyObject 与类型对象
CPython 中每个 Python 对象都是一个 C 结构体 PyObject:
// 简化的 PyObject 结构
typedef struct {
Py_ssize_t ob_refcnt; // 引用计数
PyTypeObject *ob_type; // 类型指针
} PyObject;
import sys
import ctypes
x = 42
# 查看引用计数
print(sys.getrefcount(x)) # 引用计数
# 查看对象的底层信息
print(id(x)) # 内存地址
print(type(x)) # 类型对象
print(x.__class__) # 同上
# 类型也是对象
print(type(int)) # <class 'type'>
print(type(type)) # <class 'type'> — type 是自身的实例
print(int.__bases__) # (<class 'object'>,)
print(type.__bases__) # (<class 'object'>,)
print(object.__bases__) # () — 继承链的根
类型对象的方法解析
# int 的方法来自哪里?
print(int.__dict__.keys())
# dict_keys(['__repr__', '__hash__', '__getattribute__', '__lt__', ...])
# 查看某个方法的定义位置
print(int.__add__) # <slot wrapper '__add__' of 'int' objects>
print(int.bit_length) # <method 'bit_length' of 'int' objects>
28.3 属性查找链
class Meta(type):
attr = "meta_attr"
class MyClass(metaclass=Meta):
attr = "class_attr"
def __init__(self):
self.attr = "instance_attr"
obj = MyClass()
属性查找顺序:
obj.attr 的查找过程:
1. 检查 type(obj).__mro__ 中是否有数据描述符
2. 检查 obj.__dict__(实例字典)
3. 检查 type(obj).__mro__ 中的非数据描述符和类属性
4. 调用 __getattr__(如果定义了)
5. 抛出 AttributeError
class Descriptor:
def __get__(self, obj, objtype=None):
return "descriptor_value"
def __set__(self, obj, value):
pass
class MyClass:
x = Descriptor() # 数据描述符
def __init__(self):
self.__dict__["x"] = "instance_value"
obj = MyClass()
print(obj.x) # "descriptor_value" — 数据描述符优先于实例字典
28.4 import 系统的工作机制
# import 的完整过程
# 1. 检查 sys.modules 缓存
# 2. 调用 finders 查找模块
# 3. 调用 loaders 加载模块
# 4. 在 sys.modules 中缓存
# 5. 绑定到当前命名空间
import sys
# sys.modules — 已加载模块的缓存
print("json" in sys.modules) # 可能 False
import json
print("json" in sys.modules) # True
# 删除缓存可以强制重新加载
# del sys.modules["json"]
# sys.meta_path — 模块查找器
print(sys.meta_path)
# [BuiltinImporter, FrozenImporter, PathFinder]
# sys.path_hooks — 路径钩子
print(sys.path_hooks)
自定义导入器
import sys
import importlib.abc
class JsonImporter(importlib.abc.MetaPathFinder, importlib.abc.Loader):
"""允许 import json 文件作为模块"""
def find_module(self, fullname, path=None):
import os
if os.path.exists(fullname + ".json"):
return self
return None
def load_module(self, fullname):
import json
if fullname in sys.modules:
return sys.modules[fullname]
with open(fullname + ".json") as f:
data = json.load(f)
import types
module = types.ModuleType(fullname)
module.__dict__.update(data)
sys.modules[fullname] = module
return module
# sys.meta_path.insert(0, JsonImporter())
# import config # 如果存在 config.json,则加载为模块
28.5 Python 的编译过程
源码 (.py)
│
▼ 词法分析 (Tokenizer)
Token 流
│
▼ 语法分析 (Parser)
AST (抽象语法树)
│
▼ 编译 (Compiler)
字节码 (Code Object)
│
▼ 执行 (VM)
结果
# 查看 Token
import tokenize
import io
code = "x = 1 + 2"
tokens = tokenize.generate_tokens(io.StringIO(code).readline)
for tok in tokens:
print(tok)
# TokenInfo(type=1 (NAME), string='x', ...)
# TokenInfo(type=54 (OP), string='=', ...)
# TokenInfo(type=2 (NUMBER), string='1', ...)
# ...
# 查看 AST
import ast
tree = ast.parse("x = 1 + 2")
print(ast.dump(tree, indent=2))
# Module(body=[
# Assign(targets=[Name(id='x')],
# value=BinOp(left=Constant(value=1),
# op=Add(),
# right=Constant(value=2)))
# ])
28.6 ast 模块与代码分析
遍历 AST
import ast
code = """
def add(a, b):
return a + b
def multiply(a, b):
return a * b
result = add(1, 2) + multiply(3, 4)
"""
tree = ast.parse(code)
# 查找所有函数定义
for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef):
print(f"函数: {node.name}, 行号: {node.lineno}")
# 函数: add, 行号: 2
# 函数: multiply, 行号: 5
AST 访问者模式
class FunctionAnalyzer(ast.NodeVisitor):
def __init__(self):
self.functions = []
self.calls = []
def visit_FunctionDef(self, node):
self.functions.append({
"name": node.name,
"args": [arg.arg for arg in node.args.args],
"line": node.lineno,
})
self.generic_visit(node) # 继续遍历子节点
def visit_Call(self, node):
if isinstance(node.func, ast.Name):
self.calls.append(node.func.id)
self.generic_visit(node)
analyzer = FunctionAnalyzer()
analyzer.visit(tree)
print("函数定义:", analyzer.functions)
print("函数调用:", analyzer.calls)
AST 代码转换
class DebugTransformer(ast.NodeTransformer):
"""在每个函数开头插入 print 语句"""
def visit_FunctionDef(self, node):
debug_print = ast.parse(
f'print("调用函数: {node.name}")'
).body[0]
node.body.insert(0, debug_print)
ast.fix_missing_locations(node)
return node
code = """
def hello(name):
return f"Hello, {name}!"
"""
tree = ast.parse(code)
tree = DebugTransformer().visit(tree)
ast.fix_missing_locations(tree)
# 编译并执行转换后的代码
compiled = compile(tree, "<string>", "exec")
exec(compiled)
hello("Alice")
# 调用函数: hello
# 'Hello, Alice!'
实用:代码复杂度分析
class ComplexityAnalyzer(ast.NodeVisitor):
"""计算圈复杂度(Cyclomatic Complexity)"""
def __init__(self):
self.complexity = {}
self._current_func = None
def visit_FunctionDef(self, node):
self._current_func = node.name
self.complexity[node.name] = 1 # 基础复杂度
self.generic_visit(node)
def visit_If(self, node):
if self._current_func:
self.complexity[self._current_func] += 1
self.generic_visit(node)
def visit_For(self, node):
if self._current_func:
self.complexity[self._current_func] += 1
self.generic_visit(node)
def visit_While(self, node):
if self._current_func:
self.complexity[self._current_func] += 1
self.generic_visit(node)
def visit_ExceptHandler(self, node):
if self._current_func:
self.complexity[self._current_func] += 1
self.generic_visit(node)
source = open("some_module.py").read()
tree = ast.parse(source)
analyzer = ComplexityAnalyzer()
analyzer.visit(tree)
for func, complexity in sorted(analyzer.complexity.items(), key=lambda x: -x[1]):
status = "⚠️ 复杂" if complexity > 10 else "✓"
print(f" {status} {func}: 复杂度 {complexity}")
本章小结:了解 Python 底层机制有助于写出更高效的代码,也能更好地理解语言行为。
dis模块揭示字节码层面的性能差异,属性查找链解释了描述符的优先级,ast模块让你可以分析和变换 Python 代码。这些知识不是日常必需的,但在调试疑难问题和编写高级工具时非常有价值。