首页 / 知识库 / 0基础入门-阅读资料 / 0基础-python入门到精通

第 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 代码。这些知识不是日常必需的,但在调试疑难问题和编写高级工具时非常有价值。