Python元编程与代码生成

一、什么是元编程

元编程是编写能够操作代码的代码。Python提供了丰富的元编程工具:装饰器、元类、描述符、exec/eval、AST操作等。


二、动态属性与方法

class DynamicModel:
"""动态创建属性和方法"""
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)

@classmethod
def add_method(cls, name, func):
setattr(cls, name, func)

@classmethod
def add_property(cls, name, getter, setter=None):
setattr(cls, name, property(getter, setter))

# 动态添加方法
DynamicModel.add_method('greet', lambda self: f"Hello, {self.name}")

obj = DynamicModel(name="Alice", age=30)
print(obj.greet()) # Hello, Alice

# 动态创建类
def create_model(name, fields):
"""动态创建数据模型类"""
def __init__(self, **kwargs):
for field in fields:
setattr(self, field, kwargs.get(field))

def __repr__(self):
attrs = ', '.join(f"{f}={getattr(self, f)!r}" for f in fields)
return f"{name}({attrs})"

cls = type(name, (), {
'__init__': __init__,
'__repr__': __repr__,
'_fields': fields,
})
return cls

User = create_model('User', ['name', 'email', 'age'])
user = User(name='Bob', email='bob@example.com', age=25)
print(user) # User(name='Bob', email='bob@example.com', age=25)


三、装饰器元编程

import functools
import time
import inspect

def auto_repr(cls):
"""自动生成__repr__方法"""
def __repr__(self):
params = inspect.signature(cls.__init__).parameters
attrs = [
f"{name}={getattr(self, name)!r}"
for name in params if name != 'self'
]
return f"{cls.__name__}({', '.join(attrs)})"
cls.__repr__ = __repr__
return cls

def auto_eq(cls):
"""自动生成__eq__和__hash__"""
params = list(inspect.signature(cls.__init__).parameters.keys())
params.remove('self')

def __eq__(self, other):
if not isinstance(other, cls):
return NotImplemented
return all(getattr(self, p) == getattr(other, p) for p in params)

def __hash__(self):
return hash(tuple(getattr(self, p) for p in params))

cls.__eq__ = __eq__
cls.__hash__ = __hash__
return cls

def validate_types(func):
"""运行时类型检查装饰器"""
hints = func.__annotations__

@functools.wraps(func)
def wrapper(*args, **kwargs):
# 绑定参数
sig = inspect.signature(func)
bound = sig.bind(*args, **kwargs)
bound.apply_defaults()

for param_name, value in bound.arguments.items():
if param_name in hints and hints[param_name] is not inspect.Parameter.empty:
expected_type = hints[param_name]
if not isinstance(value, expected_type):
raise TypeError(
f"参数 {param_name} 期望 {expected_type.__name__},"
f"得到 {type(value).__name__}"
)
return func(*args, **kwargs)
return wrapper

@auto_repr
@auto_eq
class Point:
def __init__(self, x, y):
self.x = x
self.y = y

@validate_types
def add(a: int, b: int) -> int:
return a + b


四、使用exec和compile

# exec执行动态代码
def create_function(name, args, body):
"""动态创建函数"""
args_str = ', '.join(args)
code = f"def {name}({args_str}):\n"
for line in body.split('\n'):
code += f" {line}\n"

namespace = {}
exec(code, namespace)
return namespace[name]

# 创建动态验证函数
validate_age = create_function(
'validate_age',
['value'],
'if not isinstance(value, int):\n raise TypeError("必须是整数")\n'
'if value < 0 or value > 150:\n raise ValueError("范围错误")\n'
'return True'
)

validate_age(25) # True

# compile用于预编译代码
code = compile('x + y', '', 'eval')
result = eval(code, {'x': 10, 'y': 20}) # 30

# 安全注意:永远不要对不信任的输入使用exec/eval


五、AST(抽象语法树)操作

import ast

# 解析代码为AST
source = """
def calculate(x, y):
result = x + y
return result * 2
"""

tree = ast.parse(source)
print(ast.dump(tree, indent=2))

# AST转换器
class DebugTransformer(ast.NodeTransformer):
"""在每个函数入口添加打印语句"""
def visit_FunctionDef(self, node):
# 创建print语句
debug_print = ast.parse(
f'print("调用函数: {node.name}")'
).body[0]

# 插入到函数体开头
node.body.insert(0, debug_print)
ast.fix_missing_locations(node)
return node

# 应用转换
transformer = DebugTransformer()
new_tree = transformer.visit(tree)
code = compile(new_tree, '', 'exec')
exec(code)

calculate(1, 2) # 输出: 调用函数: calculate

# AST分析器
class ComplexityAnalyzer(ast.NodeVisitor):
"""计算函数的圈复杂度"""
def __init__(self):
self.complexity = {}

def visit_FunctionDef(self, node):
complexity = 1 # 基础复杂度
for child in ast.walk(node):
if isinstance(child, (ast.If, ast.While, ast.For)):
complexity += 1
elif isinstance(child, ast.BoolOp):
complexity += len(child.values) - 1
elif isinstance(child, ast.ExceptHandler):
complexity += 1
self.complexity[node.name] = complexity
self.generic_visit(node)

analyzer = ComplexityAnalyzer()
analyzer.visit(ast.parse(open('module.py').read()))
for func, score in analyzer.complexity.items():
print(f"{func}: 复杂度 {score}")


六、代码生成

class CodeGenerator:
"""基于模板的代码生成器"""
def __init__(self):
self.indent_level = 0
self.lines = []

def indent(self):
self.indent_level += 1
return self

def dedent(self):
self.indent_level -= 1
return self

def line(self, code=''):
self.lines.append(' ' * self.indent_level + code)
return self

def blank(self):
self.lines.append('')
return self

def generate(self):
return '\n'.join(self.lines)

def generate_dataclass(name, fields):
"""生成dataclass代码"""
gen = CodeGenerator()
gen.line('from dataclasses import dataclass')
gen.line('from typing import Optional')
gen.blank()
gen.blank()
gen.line('@dataclass')
gen.line(f'class {name}:')
gen.indent()

for field_name, field_type, default in fields:
if default is not None:
gen.line(f'{field_name}: {field_type} = {default!r}')
else:
gen.line(f'{field_name}: {field_type}')

gen.blank()
gen.line('def to_dict(self):')
gen.indent()
gen.line('return {')
gen.indent()
for field_name, _, _ in fields:
gen.line(f"'{field_name}': self.{field_name},")
gen.dedent()
gen.line('}')

return gen.generate()

# 使用
code = generate_dataclass('User', [
('name', 'str', None),
('email', 'str', None),
('age', 'Optional[int]', None),
('active', 'bool', True),
])
print(code)


七、import钩子

import sys
import importlib.abc
import importlib.machinery

class AutoInstallFinder(importlib.abc.MetaPathFinder):
"""自动安装缺失的包"""
def find_module(self, fullname, path=None):
if fullname in sys.modules:
return None
try:
importlib.import_module(fullname)
return None
except ImportError:
return self

def load_module(self, fullname):
import subprocess
print(f"自动安装: {fullname}")
subprocess.check_call([sys.executable, '-m', 'pip', 'install', fullname])
return importlib.import_module(fullname)

# 注册(谨慎使用)
# sys.meta_path.append(AutoInstallFinder())

class TransformLoader(importlib.abc.Loader):
"""加载时转换模块代码"""
def __init__(self, source, transformer):
self.source = source
self.transformer = transformer

def exec_module(self, module):
tree = ast.parse(self.source)
tree = self.transformer.visit(tree)
ast.fix_missing_locations(tree)
code = compile(tree, module.__spec__.origin, 'exec')
exec(code, module.__dict__)


八、__class_getitem__与泛型

class TypedList:
"""支持泛型语法的列表"""
def __class_getitem__(cls, item_type):
class TypedListInstance(list):
_item_type = item_type

def append(self, item):
if not isinstance(item, self._item_type):
raise TypeError(
f"期望 {self._item_type.__name__},"
f"得到 {type(item).__name__}"
)
super().append(item)

def __repr__(self):
return f"TypedList[{self._item_type.__name__}]({list.__repr__(self)})"

return TypedListInstance

int_list = TypedList[int]()
int_list.append(1)
int_list.append(2)
# int_list.append("3") # TypeError


九、使用__prepare__控制类命名空间

from collections import OrderedDict

class OrderedMeta(type):
@classmethod
def __prepare__(mcs, name, bases):
"""返回自定义命名空间"""
return OrderedDict()

def __new__(mcs, name, bases, namespace):
cls = super().__new__(mcs, name, bases, dict(namespace))
cls._field_order = [
key for key in namespace
if not key.startswith('_') and not callable(namespace[key])
]
return cls

class Schema(metaclass=OrderedMeta):
name = "string"
age = "integer"
email = "string"

print(Schema._field_order) # ['name', 'age', 'email'] 保持定义顺序


十、总结

元编程使用建议:
1. 装饰器是最常用且最安全的元编程工具
2. 元类适合框架级别的抽象,日常开发很少需要
3. exec/eval功能强大但有安全风险,尽量避免
4. AST操作适合代码分析和转换工具
5. 代码生成适合从schema生成样板代码
6. 元编程会增加代码复杂度,只在确实需要时使用
7. 优先使用__init_subclass__替代简单的元类需求

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐