元类与描述符
High Contrast
Dark Mode
Light Mode
Sepia
Forest
1 min read204 words

元类与描述符

Python 的黑魔法——元类控制类的创建过程,描述符控制属性的访问行为,理解它们才能读懂框架源码。

元编程体系

graph TD META[Python 元编程] --> METACLASS[元类 metaclass] META --> DESC[描述符 descriptor] META --> DECO[装饰器 decorator] META --> SLOTS["__slots__"] METACLASS --> MC1[控制类的创建] METACLASS --> MC2[自动注册] METACLASS --> MC3[验证类定义] DESC --> D1["__get__"] DESC --> D2["__set__"] DESC --> D3["__delete__"] DECO --> DC1[类装饰器] DECO --> DC2[函数装饰器] style META fill:#e3f2fd,stroke:#1565c0,stroke-width:2px style METACLASS fill:#fce4ec,stroke:#c62828,stroke-width:2px

描述符

"""
描述符:控制属性的访问行为
Django ORM、SQLAlchemy、pydantic 的底层都靠它
"""
# === 数据描述符 ===
class Validated:
"""带验证的属性描述符"""
def __init__(self, validator=None, default=None):
self.validator = validator
self.default = default
def __set_name__(self, owner, name):
"""Python 3.6+:自动获取属性名"""
self.name = name
self.storage_name = f"_desc_{name}"
def __get__(self, obj, objtype=None):
if obj is None:
return self  # 类访问返回描述符本身
return getattr(obj, self.storage_name, self.default)
def __set__(self, obj, value):
if self.validator and not self.validator(value):
raise ValueError(f"{self.name}: 值 {value!r} 验证失败")
setattr(obj, self.storage_name, value)
# 使用示例
class Product:
name = Validated(validator=lambda v: isinstance(v, str) and len(v) > 0)
price = Validated(validator=lambda v: isinstance(v, (int, float)) and v > 0)
stock = Validated(validator=lambda v: isinstance(v, int) and v >= 0, default=0)
p = Product()
p.name = "Python Book"  # OK
p.price = 59.9           # OK
# p.price = -10          # ValueError: price: 值 -10 验证失败
# === 常见描述符模式 ===
class TypeChecked:
"""类型检查描述符"""
def __init__(self, expected_type):
self.expected_type = expected_type
def __set_name__(self, owner, name):
self.name = name
self.storage_name = f"_tc_{name}"
def __get__(self, obj, objtype=None):
if obj is None:
return self
return getattr(obj, self.storage_name, None)
def __set__(self, obj, value):
if not isinstance(value, self.expected_type):
raise TypeError(
f"{self.name} 期望 {self.expected_type.__name__},"
f"得到 {type(value).__name__}"
)
setattr(obj, self.storage_name, value)
class Config:
host = TypeChecked(str)
port = TypeChecked(int)
debug = TypeChecked(bool)
cfg = Config()
cfg.host = "localhost"  # OK
cfg.port = 8080         # OK
# cfg.port = "8080"     # TypeError: port 期望 int,得到 str

元类

"""
元类:控制类的创建过程
"""
# === 基础概念 ===
# 在 Python 中,类也是对象
# type 是所有类的元类
print(type(int))    # <class 'type'>
print(type(str))    # <class 'type'>
# === 自定义元类 ===
class RegistryMeta(type):
"""自动注册所有子类的元类"""
_registry: dict[str, type] = {}
def __new__(mcs, name, bases, namespace):
cls = super().__new__(mcs, name, bases, namespace)
if bases:  # 跳过基类本身
RegistryMeta._registry[name] = cls
return cls
@classmethod
def get_registry(mcs) -> dict[str, type]:
return dict(mcs._registry)
class Plugin(metaclass=RegistryMeta):
"""插件基类"""
def execute(self):
raise NotImplementedError
class EmailPlugin(Plugin):
def execute(self):
return "发送邮件"
class SMSPlugin(Plugin):
def execute(self):
return "发送短信"
# 自动注册,无需手动维护列表
print(RegistryMeta.get_registry())
# {'EmailPlugin': <class 'EmailPlugin'>, 'SMSPlugin': <class 'SMSPlugin'>}
# 动态实例化
for name, cls in RegistryMeta.get_registry().items():
print(f"{name}: {cls().execute()}")
# === __init_subclass__(更简洁的替代方案,推荐)===
class Handler:
_handlers: dict[str, type] = {}
def __init_subclass__(cls, event: str = "", **kwargs):
super().__init_subclass__(**kwargs)
if event:
Handler._handlers[event] = cls
@classmethod
def dispatch(cls, event: str, **kwargs):
handler_cls = cls._handlers.get(event)
if handler_cls:
return handler_cls().handle(**kwargs)
raise ValueError(f"未知事件: {event}")
class UserCreated(Handler, event="user.created"):
def handle(self, **kwargs):
return f"处理用户创建: {kwargs}"
class OrderPaid(Handler, event="order.paid"):
def handle(self, **kwargs):
return f"处理订单支付: {kwargs}"
print(Handler.dispatch("user.created", name="Alice"))
print(Handler.dispatch("order.paid", order_id=123))

__slots__ 优化

"""
__slots__:限制属性 + 省内存
"""
from sys import getsizeof
class RegularUser:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
class SlottedUser:
__slots__ = ("name", "age")
def __init__(self, name: str, age: int):
self.name = name
self.age = age
# 内存对比
r = RegularUser("Alice", 30)
s = SlottedUser("Alice", 30)
print(f"普通类: {getsizeof(r) + getsizeof(r.__dict__)} bytes")
print(f"Slots: {getsizeof(s)} bytes")  # 少约 40-50%
# slots 不能动态添加属性
# s.email = "test"  # AttributeError

何时使用

技术 使用场景 复杂度 替代方案
描述符 属性验证、ORM 字段 @property
元类 框架级自动注册 __init_subclass__
__slots__ 大量实例省内存 dataclass
类装饰器 增强类行为 元类

本章小结

知识点 要点
描述符 __get__ / __set__ 控制属性访问
元类 type.__new__ 控制类创建
__init_subclass__ 元类的轻量替代,推荐优先使用
__slots__ 固定属性、省 40%+ 内存
实际应用 ORM、验证框架、插件系统

下一章:函数式编程——迭代器协议与工具链。