元类和描述符是 Python 中用于元编程的高级特性——用来控制类的行为方式以及属性访问的方式。它们驱动了许多框架(ORM、Pydantic)中的“魔法”,尽管你在应用代码中很少直接编写它们。
元类——“创建类的类”
python
:
(Dog)
(Dog())
正如类创建实例一样,元类创建类。通过自定义元类,你可以拦截并修改类的创建过程:
class Meta(type):
def __new__(mcs, name, bases, namespace):
# runs when a class USING this metaclass is DEFINED
namespace["created_by"] = "Meta" # inject an attribute into every such class
return super().__new__(mcs, name, bases, namespace)
class MyClass(metaclass=Meta):
pass
MyClass.created_by # "Meta" — injected by the metaclass at class-creation time
这让框架能够自动注册类、校验定义,或者为每个子类添加行为——例如 ORM 如何把类属性转化为数据库列。
描述符是定义了 __get__/__set__/__delete__ 的对象,用于控制读取或写入属性时所发生的行为:
class Positive:
def __set_name__(self, owner, name):
self.name = f"_{name}"
def __get__(self, obj, objtype=None):
return getattr(obj, self.name)
def __set__(self, obj, value):
if value < 0:
raise ValueError("must be positive") # validate on assignment
setattr(obj, self.name, value)
class Account:
balance = Positive() # a descriptor as a class attribute
a = Account()
a.balance = 100 # calls Positive.__set__ → validates
a.balance = -5 # ❌ ValueError
描述符会拦截属性访问——从而实现校验、计算属性和惰性加载。事实上,@property、@classmethod 和 @staticmethod 都是以描述符的形式实现的。
Metaclasses → Django/SQLAlchemy ORM models, ABCs, Pydantic, framework registration
Descriptors → @property, ORM fields, validation libraries, lazy attributes
元类和描述符是 Python 许多最强大的库背后的元编程机制——比如把类定义转化为数据库 schema 的 ORM、像 Pydantic 这样的校验框架,乃至像 @property(一个描述符)这样的内置功能。
虽然大多数开发者很少直接编写它们(在更简单的工具足够用时也不应该动用它们),但理解它们能够揭开这些框架在类级别定制和受控属性访问上所谓“魔法”的神秘面纱。
这些知识对于高级库设计、调试框架行为以及深入理解 Python 的对象模型都很有价值——正是它将对语言的中级理解与专家级理解区分开来。