元编程,简单来说就是用代码操控代码。Python 拥有丰富的元编程工具:type() 可以动态生成类;元类(metaclass)能够干预类的创建全过程;描述符协议则实现对属性访问行为的拦截。
我们日常使用的 @property、各类 ORM 框架、类装饰器等高级特性,底层实现都依赖这套元编程机制。
在 Python 中一切皆对象,类本身同样是对象。
既然类是对象,那它必然由某个类实例化而来,这个 “制造类的类” 就是 type。
type 拥有双重身份:
(1)普通内置函数:type(obj),传入一个对象,返回该对象的类型。
print(type(123)) # int
print(type("hi")) # str(2)元类(metaclass):type 是所有普通类默认的元类,负责创建类。语法:
type(类名, 父类元组, 命名空间字典)使用 type() 可以动态构建类。
# 和 class User: 定义效果等价
User = type("User", (), {"name": "test"})底层逻辑:
class User:
pass
# User 这个类对象,是 type 的实例
print(type(User)) # type
# int、str 等内置类型,同样是 type 的实例
print(type(int)) # type其中,对象由类创建,类由元类 type 创建。而 type 自身是自己的实例,即 type(type) 等于 type(元编程闭环)。
日常的 class 语法只是语法糖,Python 执行 class 语句时,底层调用元类 type 来实例化生成类对象。
而自定义元类,本质就是继承 type,重写类创建逻辑,以此拦截、修改类的构建过程。
type() 语法如下:
type(name, bases, namespace)等价于 class 关键字定义类,class 只是语法糖,底层最终调用该形式的 type。
参数详解:
name: str 类的名称,对应类的 __name__ 属性。例如:
Cls = type("Demo", (), {})
print(Cls.__name__) # Demobases: tuple [类,...] 父类元组,对应类的继承列表 __bases__,空元组代表不继承任何类(默认继承 object)。例如:
class Base:
pass
# 继承 Base
Sub = type("Sub", (Base,), {})
print(Sub.__bases__) # (Base,)namespace: dict 类命名空间字典,存放类属性、方法、特殊魔法属性,最终成为类的 __dict__。
示例:使用 type() 创建一个类
# 传统写法
class Dog:
species = "Canis"
def __init__(self, name):
self.name = name
def bark(self):
return f"{self.name} says woof!"
# 等价于 type() 动态创建
Dog2 = type(
"Dog", # 类名
(object,), # 父类元组
{
"species": "Canis",
"__init__": lambda self, name: setattr(self, "name", name),
"bark": lambda self: f"{self.name} says woof!",
},
)
d1 = Dog("Buddy")
print(d1.bark()) # Buddy says woof!
print(d1.species) # Canis
print(type(d1)) # <class '__main__.Dog'>
d2 = Dog2("Buddy")
print(d2.bark()) # Buddy says woof!
print(d2.species) # Canis
print(type(d2)) # <class '__main__.Dog'>运行示例,输出:
Buddy says woof!
Canis
<class '__main__.Dog'>
Buddy says woof!
Canis
<class '__main__.Dog'>下面示例利用内置 type() 动态创建类,新类自动继承 Animal 类,工厂函数 make_species_class 批量生成不同动物物种类。代码如下:
# 父类:动物基类
class Animal:
# 类属性:动物界
kingdom = "Animalia"
def __init__(self, name):
"""构造方法,初始化动物名称"""
self.name = name
# 工厂函数:动态创建继承 Animal 的子类
def make_species_class(species_name, sound):
"""
工厂函数:根据物种名动态创建继承Animal的类
:param species_name: 物种类名
:param sound: 该物种的叫声
:return: 动态生成的物种类
"""
def __init__(self, name):
# 调用父类构造方法
Animal.__init__(self, name)
# 保存当前物种对应的叫声
self.sound = sound
def speak(self):
"""实例方法:发出叫声"""
return f"{self.name} says {self.sound}"
# type(类名, 父类元组, 命名空间字典) 动态构建类
return type(
species_name, # 生成的类名称
(Animal,), # 继承的父类元组,实现继承
{
"__init__": __init__, # 绑定构造函数
"speak": speak, # 绑定实例方法
"_species": species_name # 自定义类属性,标记物种名称
},
)
# 动态生成 Cat 类、Dog 类
Cat = make_species_class("Cat", "meow")
Dog = make_species_class("Dog", "woof")
# 实例化对象
c = Cat("Kitty")
d = Dog("Buddy")
print(c.speak()) # Kitty says meow
print(d.speak()) # Buddy says woof
print(c.kingdom) # Animalia(继承自 Animal 的类属性)
print(isinstance(c, Animal)) # True,Cat实例属于Animal类型
print(issubclass(Cat, Dog)) # False,Cat与Dog是同级兄弟类,不存在继承关系注意,Cat、Dog 是两个独立生成的子类,共同父类是 Animal,互为兄弟类,互相不存在继承关系。每个类自带独立的 speak 行为与 _species 标识。
Python 的类、对象都是可变对象,程序运行过程中,可以随时增删、修改类属性、实例方法、静态方法、类方法,不需要重新定义 class,也不需要 type () 新建类。
示例:
class Calculator:
def add(self, a, b):
return a + b
c = Calculator()
print(c.add(1, 2)) # 3
# 运行时给类添加新方法
Calculator.sub = lambda self, a, b: a - b
print(c.sub(5, 3)) # 2(已有实例自动获得新方法)
# 运行时替换方法
Calculator.add = lambda self, a, b: a * b
print(c.add(1, 2)) # 2(方法已被替换)
# 动态添加属性到单个实例(不影响类)
c.factor = 10
print(c.factor) # 10
# print(Calculator.factor) # AttributeError注意:
动态修改类(Monkey Patch)只在测试打桩、热修复等场景,需要谨慎使用。
生产代码中过度使用动态修改会降低可维护性。
描述符 = 拦截器
描述符是实现了 __get__、__set__ 或 __delete__ 方法的类的实例。
当访问宿主实例属性时:
读取属性前触发 __get__
赋值属性前触发 __set__
del 删除属性前触发 __delete__
注意,不是属性赋值之后,是赋值操作发生时优先拦截,你可以在校验不通过时直接拒绝赋值。只有描述符挂载为宿主类属性,拦截逻辑才生效。如果放在 __init__ 当成实例属性,协议不会触发。
Python 的 @property、@staticmethod、@classmethod 底层都是描述符。
示例,演示描述符三协议方法的用法。
class Descriptor:
"""最简描述符示例"""
def __init__(self, name):
self.name = name
def __get__(self, instance, owner):
"""
instance: 被访问的实例(通过类访问时为 None)
owner: 拥有该描述符的类
"""
if instance is None:
return f"<Descriptor {self.name} for {owner.__name__}>"
return instance.__dict__.get(self.name, f"[default for {self.name}]")
def __set__(self, instance, value):
"""拦截属性赋值"""
print(f"[SET] {instance.__class__.__name__}.{self.name} = {value!r}")
instance.__dict__[self.name] = value
def __delete__(self, instance):
"""拦截属性删除"""
print(f"[DELETE] {instance.__class__.__name__}.{self.name}")
if self.name in instance.__dict__:
del instance.__dict__[self.name]
class Person:
# 类属性是描述符实例,所有 Person 实例共享同一个描述符对象
name = Descriptor("name")
age = Descriptor("age")
if __name__ == "__main__":
p = Person()
# 赋值触发 __set__
p.name = "Alice" # [SET] Person.name = 'Alice'
p.age = 30 # [SET] Person.age = 30
# 读取触发 __get__
print(p.name) # Alice
print(p.age) # 30
# 删除触发 __delete__
del p.name # [DELETE] Person.name
# 通过类访问
print(Person.name) # <Descriptor name for Person>数据描述符(get + set)可读、可写双向拦截。赋值、取值都会经过描述符,优先级高于实例自身 __dict__,典型代表 @property。
非数据描述符(仅 get,没有 set)只拦截读取,不拦截赋值。一旦你手动给实例绑定同名属性,实例字典的值会覆盖描述符。典型代表 @staticmethod、@classmethod。
两者区别如下表:
| 类型 | 条件 | 优先级 |
| 数据描述符 | 定义了 __set__ 或 __delete__ | 高(实例字典也覆盖不了) |
| 非数据描述符 | 只定义了 __get__ | 低(实例字典中的同名属性会覆盖它) |
示例:
# 数据描述符(有 __set__)
class DataDescriptor:
def __init__(self, name):
self.name = name
def __get__(self, instance, owner):
return f"[DataDesc] {self.name}"
def __set__(self, instance, value):
print(f"[DataDesc] set {self.name} = {value}")
# 非数据描述符(只有 __get__)
class NonDataDescriptor:
def __init__(self, name):
self.name = name
def __get__(self, instance, owner):
return f"[NonDataDesc] {self.name}"
class Demo:
data = DataDescriptor("data")
nondata = NonDataDescriptor("nondata")
if __name__ == "__main__":
d = Demo()
# 数据描述符:实例字典无法覆盖
print(d.data) # [DataDesc] data
d.__dict__["data"] = "instance value"
print(d.data) # 仍然是 [DataDesc] data(数据描述符优先级更高)
# 非数据描述符:实例字典可以覆盖
print(d.nondata) # [NonDataDesc] nondata
d.__dict__["nondata"] = "instance value"
print(d.nondata) # instance value(实例字典优先)
# 删除实例字典后,恢复描述符行为
del d.__dict__["nondata"]
print(d.nondata) # [NonDataDesc] nondataPython 属性查找优先级(MRO):
数据描述符(定义了 __set__/__delete__)
实例字典 obj.__dict__
非数据描述符(只定义 __get__)
类字典 cls.__dict__ 及 MRO
示例 2:用描述符实现类型检查属性
from typing import Any
class Typed:
"""强制类型检查的描述符"""
def __init__(self, name: str, expected_type: type):
self.name = name
self.expected_type = expected_type
def __get__(self, instance: Any, owner: type) -> Any:
if instance is None:
return self
return instance.__dict__.get(self.name)
def __set__(self, instance: Any, value: Any) -> None:
if not isinstance(value, self.expected_type):
raise TypeError(
f"属性 {self.name} 期望类型 {self.expected_type.__name__}, "
f"实际得到 {type(value).__name__}"
)
instance.__dict__[self.name] = value
def __delete__(self, instance: Any) -> None:
del instance.__dict__[self.name]
class TypedMeta(type):
"""元类:自动为类属性添加 Typed 描述符"""
# __new__ 负责开辟内存、产出全新实例;__init__ 只负责填充实例属性
# 属性说明:
# mcs(meta class):元类自身,等价于普通 __new__ 的 cls
# name:将要生成的类名称(字符串)
# bases:父类元组 (Base1, Base2,...),就是类定义的继承列表
# namespace:类的命名空间字典,存放方法、类属性 {"__init__":..., "attr":123}
def __new__(mcs, name, bases, namespace):
# 从类命名空间 namespace 中获取注解字典 __annotations__
# 如果 age:int,则 __annotations__ = {'age': int}
annotations = namespace.get("__annotations__", {})
for attr_name, attr_type in annotations.items():
# 以_开头的属性跳过
if attr_name.startswith("_"):
continue
# 为非_开头的属性自动添加 Typed
namespace[attr_name] = Typed(attr_name, attr_type)
return super().__new__(mcs, name, bases, namespace)
class User(metaclass=TypedMeta):
name: str
age: int
score: float
def __init__(self, name: str, age: int, score: float):
self.name = name
self.age = age
self.score = score
if __name__ == "__main__":
u = User("Alice", 30, 95.5)
print(u.name, u.age, u.score)
try:
u.age = "30" # TypeError: 属性 age 期望类型 int, 实际得到 str
except TypeError as e:
print(e)在 Python 中,对象由类创建,类由元类创建。例如:
obj = Class() 类创建实例对象
Cls = type(...) 元类创建类本身
type 就是 Python 默认元类,所有普通类,顶层元类都是 type。例如:
# 默认元类是 type
class Foo:
pass
print(type(Foo)) # <class 'type'>
# 显式指定元类为 type(等价于不写)
class Bar(type):
pass
class Baz(metaclass=type):
pass
print(type(Baz)) # <class 'type'>自定义元类需要重写 __new__(创建类对象)或 __init__(初始化类对象)。方法定义如下:
__new__(mcs, name, bases, namespace):负责创建类对象(分配内存,产出类),参数如下:
mcs:元类本身
name:待创建的类名(字符串)
bases:父类组成的元组
namespace:类的命名空间字典,存放属性、方法、注解
__init__(cls, name, bases, namespace):类对象已经创建完毕,对类做初始化配置,参数如下:
cls:已经创建完成的目标类对象(重点区分元类 mcs)
name:类名字符串
bases:父类元组
namespace:类定义时的原始命名空间字典
例如:
class SingletonMeta(type):
"""单例元类:确保每个类只有一个实例"""
_instances = {}
# 执行 类名(参数) 实例化对象时触发:
# obj = Demo(1,2) → 调用元类的 __call__
# 参数如下:
# cls:当前要实例化的类(不是元类,是你 Class() 要创建实例的那个类)
# *args, **kwargs:实例化时传入的参数 obj = cls(*args, **kwargs)
def __call__(cls, *args, **kwargs):
"""控制类实例化过程"""
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Database(metaclass=SingletonMeta):
def __init__(self, url: str):
self.url = url
print(f"[INIT] Database created with url={url}")
def query(self, sql: str) -> str:
return f"Result of: {sql}"
# 测试单例
db1 = Database("postgresql://localhost/db1")
db2 = Database("postgresql://localhost/db2")
print(db1 is db2) # True
print(db1.url) # postgresql://localhost/db1(第二次参数被忽略)
print(db2.query("SELECT *"))运行代码,输出:
[INIT] Database created with url=postgresql://localhost/db1
True
postgresql://localhost/db1
Result of: SELECT *下面利用自定义元类 PluginMeta 实现插件自动注册机制。
元类在类定义阶段的 __new__ 方法拦截类创建过程,通过判断是否存在父类,自动将所有继承自 BasePlugin 的插件子类存入注册表 registry,基类本身不会被注册。
BasePlugin 作为统一插件父类,规范插件接口,要求子类实现 run 方法;UpperPlugin、ReversePlugin、PrefixPlugin 是具体功能插件,仅需继承基类即可完成自动注册,无需手动登记。
该模式适合插件系统、策略处理器开发,新增功能只需新增插件子类,框架自动发现并管理,实现代码解耦与插件即插即用。
完整代码如下:
class PluginMeta(type):
"""自动注册子类的元类"""
# 注册表:key=类名字符串,value=插件类对象
registry = {}
def __new__(mcs, name, bases, namespace):
"""
元类__new__:创建类对象时触发
:param mcs: 元类自身 PluginMeta
:param name: 待创建的类名
:param bases: 当前类继承的父类元组
:param namespace: 类命名空间字典
:return: 新生成的类对象
"""
# 调用父类元类type,正式生成类
cls = super().__new__(mcs, name, bases, namespace)
# bases不为空说明存在父类,代表是子类;过滤最顶层基类(无父类)
if bases:
# 将子类存入元类注册表
mcs.registry[name] = cls
print(f"[REGISTER] 插件 {name} 已注册")
return cls
class BasePlugin(metaclass=PluginMeta):
"""所有插件的基类,绑定注册元类"""
name = ""
def run(self, data: str) -> str:
"""插件执行入口,子类必须实现"""
raise NotImplementedError("插件需要重写 run 方法")
class UpperPlugin(BasePlugin):
"""转大写插件"""
name = "upper"
def run(self, data: str) -> str:
return data.upper()
class ReversePlugin(BasePlugin):
"""字符串反转插件"""
name = "reverse"
def run(self, data: str) -> str:
return data[::-1]
class PrefixPlugin(BasePlugin):
"""增加前缀插件"""
prefix = "[PRE]"
def run(self, data: str) -> str:
return f"{self.prefix} {data}"
print("\n已注册插件:")
for name, cls in PluginMeta.registry.items():
print(f" - {name}: {cls}")
# 根据注册表动态实例化插件并执行
text = "hello"
for name, cls in PluginMeta.registry.items():
p = cls()
print(f"{name}: {p.run(text)}")运行代码,输出:
[REGISTER] 插件 UpperPlugin 已注册
[REGISTER] 插件 ReversePlugin 已注册
[REGISTER] 插件 PrefixPlugin 已注册
已注册插件:
- UpperPlugin: <class '__main__.UpperPlugin'>
- ReversePlugin: <class '__main__.ReversePlugin'>
- PrefixPlugin: <class '__main__.PrefixPlugin'>
UpperPlugin: HELLO
ReversePlugin: olleh
PrefixPlugin: [PRE] hello下面代码自定义了元类 NamingMeta,利用元类在类创建阶段拦截类的构建流程,自动校验类内命名规范,过滤保留的 __xxx__魔术方法。
要求所有可调用的方法名称必须是小写蛇形命名(snake_case),普通公有类常量必须全部大写。
当定义类并指定 metaclass=NamingMeta 时,在代码加载、类创建瞬间完成校验,一旦出现不符合命名规则的属性或方法,直接抛出 TypeError,从底层强制统一代码规范,避免命名风格混乱。
完整代码:
class NamingMeta(type):
"""强制属性和方法命名规范:
- 类公有常量必须全大写
- 方法必须使用 snake_case 小写蛇形命名
在类创建阶段自动校验,命名违规直接抛出异常
"""
def __new__(mcs, name, bases, namespace):
"""
元类__new__:类对象创建时触发,统一校验类内成员命名
:param mcs: 元类自身 NamingMeta
:param name: 待创建的类名称
:param bases: 父类元组
:param namespace: 原始类命名空间字典(存放所有属性、方法)
:return: 校验通过后生成的新类对象
"""
new_namespace = {}
# 遍历类中所有属性、方法
for attr_name, attr_value in namespace.items():
# 魔术方法(__init__/__str__等)不做命名校验,直接保留
if attr_name.startswith("__") and attr_name.endswith("__"):
new_namespace[attr_name] = attr_value
continue
# 判断是可调用对象(函数/方法),方法名必须全小写(snake_case)
if callable(attr_value) and not attr_name.islower():
raise TypeError(
f"类 {name} 的方法 {attr_name} 必须使用 snake_case"
)
# 普通公有类常量规则:
# 不是方法、不以_开头(非私有)、名称不全大写 → 违规
if (not callable(attr_value) and
not attr_name.startswith("_") and
attr_name.upper() != attr_name):
raise TypeError(
f"类 {name} 的类常量 {attr_name} 必须全大写"
)
new_namespace[attr_name] = attr_value
# 使用校验完成的命名空间构建类对象
return super().__new__(mcs, name, bases, new_namespace)
# 绑定命名校验元类,创建类时自动执行命名检查
class Config(metaclass=NamingMeta):
MAX_SIZE = 100 # 符合规范:公有类常量全大写
TIMEOUT = 30 # 符合规范
def __init__(self):
self.data = []
def load_data(self): # 符合规范:方法采用 snake_case
pass
def process_item(self): # 符合规范
pass
# 取消注释会触发 TypeError,命名不合法示例
# class BadConfig(metaclass=NamingMeta):
# max_size = 100 # 错误:公有类常量未全大写
# def LoadData(self): # 错误:方法名不是小写snake_case
# pass
print("命名规范检查通过")该示例结合元类 + 描述符,实现一个支持字段定义、类型校验和 CRUD 的微型 ORM。
Field 作为描述符,负责拦截模型字段的读写。
ModelMeta 自定义元类,在模型类定义阶段自动扫描收集所有 Field 字段、自动生成数据表名、动态注入__init__构造方法,并完成模型全局注册,同时提供内存字典模拟数据库存储。顶层 Model 作为 ORM 基类,封装 create、all、get、filter、save 等通用增查接口。
业务只需继承 Model 并声明 Field 字段即可定义数据模型,无需重复编写样板代码。所有数据保存在内存中,支持基于字段条件的单条查询、批量过滤,完整演示了主流 ORM 底层依靠元类自动解析模型字段的核心原理。
完整代码如下:
from typing import Any, Dict, List, Optional, Type
class Field:
"""ORM 字段描述符
作为模型类属性,拦截字段的读取与赋值操作,管理实例字段数据存储
"""
def __init__(self, name: str, field_type: str, primary_key: bool = False):
# 数据库字段名
self.name = name
# 数据库字段类型(INTEGER/VARCHAR/TEXT)
self.field_type = field_type
# 是否为主键
self.primary_key = primary_key
def __get__(self, instance: Any, owner: Type) -> Any:
"""描述符取值方法
instance=None:通过类名访问字段,返回Field对象本身
instance存在:从实例字典读取字段值
"""
if instance is None:
return self
return instance.__dict__.get(self.name)
def __set__(self, instance: Any, value: Any) -> None:
"""描述符赋值方法:将字段值存入实例自身__dict__"""
instance.__dict__[self.name] = value
class ModelMeta(type):
"""ORM 自定义元类
在模型类定义阶段自动扫描Field字段、生成数据表名、注册模型、动态生成构造方法
"""
# 模型注册表:key=模型类名,value=模型类
_registry: Dict[str, Type] = {}
# 内存模拟数据库:key=数据表名,value=数据表内所有记录
_db: Dict[str, List[Dict]] = {}
def __new__(mcs, name, bases, namespace):
"""元类__new__:创建模型类对象时触发"""
# 先构建原始类对象
cls = super().__new__(mcs, name, bases, namespace)
# 过滤基类Model,仅处理业务模型子类(User/Post)
if name == "Model":
return cls
# 遍历类命名空间,收集所有Field类型字段
fields = {}
for attr_name, attr_value in namespace.items():
if isinstance(attr_value, Field):
fields[attr_name] = attr_value
cls._fields = fields
# 自动生成表名:类名小写+s 例 User → users
cls._table_name = name.lower() + "s"
# 将模型存入全局注册表
mcs._registry[name] = cls
# 在内存数据库初始化一张空数据表
mcs._db[cls._table_name] = []
# 动态生成 __init__ 构造函数,支持关键字参数初始化字段
def auto_init(self, **kwargs):
for field_name, field in fields.items():
setattr(self, field_name, kwargs.get(field_name))
cls.__init__ = auto_init
return cls
@property
def db(cls) -> List[Dict]:
"""类属性:快捷获取当前模型对应的内存数据表"""
return ModelMeta._db.get(cls._table_name, [])
class Model(metaclass=ModelMeta):
"""ORM 模型基类
所有业务模型继承此类,封装通用数据库CRUD操作(内存版)
"""
@classmethod
def create(cls, **kwargs) -> "Model":
"""类方法:新建并保存一条记录,返回模型实例"""
instance = cls(**kwargs)
# 提取所有字段构成数据库记录字典
record = {k: getattr(instance, k) for k in cls._fields}
cls.db.append(record)
instance._id = len(cls.db) # 简易模拟自增主键ID
return instance
@classmethod
def all(cls) -> List[Dict]:
"""查询表内全部数据"""
return cls.db
@classmethod
def get(cls, **filters) -> Optional[Dict]:
"""单条查询:按条件匹配,返回第一条符合条件记录,无结果返回None"""
for record in cls.db:
if all(record.get(k) == v for k, v in filters.items()):
return record
return None
@classmethod
def filter(cls, **filters) -> List[Dict]:
"""条件批量查询,返回所有匹配的记录列表"""
return [
r for r in cls.db
if all(r.get(k) == v for k, v in filters.items())
]
def save(self) -> None:
"""实例方法:将当前实例数据新增存入内存表(简化版仅支持追加,无更新逻辑)"""
record = {k: getattr(self, k) for k in self._fields}
self.__class__.db.append(record)
def __repr__(self) -> str:
"""格式化打印模型实例,方便调试查看字段"""
attrs = ", ".join(f"{k}={getattr(self, k)!r}" for k in self._fields)
return f"{self.__class__.__name__}({attrs})"
# 定义业务数据模型,继承Model,使用Field声明数据库字段
class User(Model):
id = Field("id", "INTEGER", primary_key=True)
name = Field("name", "VARCHAR")
email = Field("email", "VARCHAR")
age = Field("age", "INTEGER")
class Post(Model):
id = Field("id", "INTEGER", primary_key=True)
title = Field("title", "VARCHAR")
content = Field("content", "TEXT")
user_id = Field("user_id", "INTEGER")
# ORM 使用示例
if __name__ == "__main__":
# 创建用户记录
u1 = User.create(name="Alice", email="alice@example.com", age=30)
u2 = User.create(name="Bob", email="bob@example.com", age=25)
print("所有用户:", User.all())
# 单条件查询
found = User.get(name="Alice")
print("查询 Alice:", found)
# 批量条件过滤
adults = User.filter(age=30)
print("30岁用户:", adults)
# 创建文章记录
p1 = Post.create(title="Hello", content="World", user_id=1)
print("所有文章:", Post.all())
# 查看元类自动生成信息
print("\n注册模型:", list(ModelMeta._registry.keys()))
print("User 字段:", list(User._fields.keys()))
print("Post 表名:", Post._table_name)运行代码,输出:
所有用户: [{'id': None, 'name': 'Alice', 'email': 'alice@example.com', 'age': 30}, {'id': None, 'name': 'Bob', 'email': 'bob@example.com', 'age': 25}]
查询 Alice: {'id': None, 'name': 'Alice', 'email': 'alice@example.com', 'age': 30}
30岁用户: [{'id': None, 'name': 'Alice', 'email': 'alice@example.com', 'age': 30}]
所有文章: [{'id': None, 'title': 'Hello', 'content': 'World', 'user_id': 1}]
注册模型: ['User', 'Post']
User 字段: ['id', 'name', 'email', 'age']
Post 表名: postsPython3 的元类支持 __prepare__ 方法,返回一个字典(或自定义映射)用于存储类的命名空间。这是类定义体执行前的钩子。例如:
class OrderedMeta(type):
"""记录类属性定义顺序的元类"""
@classmethod
def __prepare__(mcs, name, bases, **kwargs):
"""
类定义执行前自动调用
自定义类代码块使用的命名空间对象
返回 OrderedDict,保证属性写入时保留源码定义顺序
"""
from collections import OrderedDict
return OrderedDict()
def __new__(mcs, name, bases, namespace):
"""
创建类对象;namespace 就是上面 __prepare__ 返回的有序字典
将有序字典转为普通dict用于构造类,并把属性顺序存入类属性 _field_order
"""
cls = super().__new__(mcs, name, bases, dict(namespace))
# 保存源码中属性、方法自上而下的定义顺序
cls._field_order = list(namespace.keys())
return cls
class Schema(metaclass=OrderedMeta):
# 属性自上而下的顺序会被元类捕获
name = ""
age = 0
email = ""
def __init__(self):
pass
# 打印类内成员源码定义顺序
print(Schema._field_order)运行代码,输出如下:
['__module__', '__qualname__', '__firstlineno__', 'name', 'age', 'email', '__init__', '__static_attributes__']更多元编程参考官方文档。