元类与描述符
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、验证框架、插件系统 |
下一章:函数式编程——迭代器协议与工具链。