44.类和元类
44.类和元类
类是一个运行时对象,它创建实例、存储属性、参与继承并通过 Python 对象模型定义行为。在 CPython 中,类是一个对象,其类型通常为type。
元类是类的类。它控制类对象的创建、初始化、表示和调用方式。
对于普通班级:```python id="g3fkzi" class User: pass
print(type(User))
输出:text id="iqt3cc"
<class 'type'>
```这意味着User是一个对象,它的类型是type。
44.1 类是对象
类定义创建一个类对象。```python id="8zc5m6" class User: name = "anonymous"
def hello(self):
return "hello"
执行后,`User`是绑定在周围名称空间中的普通名称。python id="rx6c1m"
print(User)
print(type(User))
print(User.name)
print(User.dict)
类对象存储以下属性:text id="xxewj0"
name
qualname
module
dict
bases
mro
methods
descriptors
class variables
annotations
## 44.2 类定义即执行
类体是可执行代码。```python id="938wkx"
class Example:
print("inside class body")
x = 1 + 2
```定义期间的输出:```text id="jiq7he"
inside class body
```当 CPython 执行时,类体立即运行`class`陈述。它不会延迟到创建实例为止。
这很重要,因为类体内的任何顶级代码都在类创建时运行:```python id="aj5kyg"
class Bad:
data = load_large_file()
```当定义类时,这项工作就会发生。
## 44.3 类创建管道
一个类的定义:```python id="yzw9ss"
class User(Base):
x = 1
def hello(self):
return "hello"
```在概念上类似于:```python id="g55w7a"
namespace = {}
namespace["x"] = 1
namespace["hello"] = function_object
User = type("User", (Base,), namespace)
```真正的过程有更多步骤:```text id="zd6xb1"
1. Evaluate base classes.
2. Determine the metaclass.
3. Ask the metaclass for a class namespace.
4. Execute the class body in that namespace.
5. Create the class object.
6. Call descriptor __set_name__ methods.
7. Call subclass initialization hooks.
8. Bind the class object to its name.
```该管道解释了元类、描述符、装饰器和继承如何交互。
## 44.4 评估基类
在:```python id="zu9u35"
class User(Model):
pass
```CPython 首先评估`Model`。
基类是表达式:```python id="fnc7gv"
class User(get_base_class()):
pass
```函数调用发生在创建类对象之前。
多个碱基从左到右评估:```python id="qtk75l"
class C(A(), B()):
pass
```生成的对象必须是有效的基类或者可以通过以下方式进行转换`__mro_entries__`。
## 44.5`__mro_entries__`钩子`__mro_entries__`允许非类基对象在类创建期间替换自身。
这是由一些打字和通用机器使用的。
形状示例:```python id="esjxp2"
class BaseAlias:
def __mro_entries__(self, bases):
return (RealBase,)
class C(BaseAlias()):
pass
```从概念上讲,CPython 转变为:```python id="154gd3"
class C(BaseAlias()):
pass
```进入:```python id="kfk2ta"
class C(RealBase):
pass
```出于继承目的。
大多数应用程序代码从未实现`__mro_entries__`,但它是类创建的一部分。
## 44.6 确定元类
元类可以显式指定:```python id="o3wmta"
class User(metaclass=Meta):
pass
```如果未指定元类,CPython 将从基类派生它。
对于普通班级:```python id="oe50zn"
class User:
pass
```元类是:```text id="ythfx3"
type
```对于子类:```python id="t5oe3k"
class Child(Base):
pass
```元类通常是`type(Base)`或兼容的派生元类。
所选元类必须与所有基类的元类兼容。否则 CPython 会引发元类冲突。
## 44.7 元类冲突
当基类需要不兼容的元类时,就会发生元类冲突。
例子:```python id="dk85qy"
class MetaA(type):
pass
class MetaB(type):
pass
class A(metaclass=MetaA):
pass
class B(metaclass=MetaB):
pass
class C(A, B):
pass
```这会引发错误,因为 CPython 无法选择与两者兼容的单个元类`MetaA`和`MetaB`。
通常的解决方法是定义一个组合元类:```python id="7bm0x5"
class MetaC(MetaA, MetaB):
pass
class C(A, B, metaclass=MetaC):
pass
```组合使用元类的框架时,元类冲突很常见。
## 44.8 准备类命名空间
在执行类主体之前,CPython 会向元类询问命名空间。
它通过调用来做到这一点`__prepare__`如果存在的话。```python id="0o8dg6"
class Meta(type):
@classmethod
def __prepare__(mcls, name, bases, **kwargs):
return {}
class User(metaclass=Meta):
x = 1
```返回的对象用作类主体的本地命名空间。
从历史上看,这启用了有序的类名称空间。现代词典保留插入顺序,但是`__prepare__`仍然支持自定义命名空间行为。
示例用例:```text id="rq5a86"
tracking declaration order
rejecting duplicate names
collecting field definitions
custom class DSLs
framework model declarations
```## 44.9 类主体命名空间
类主体使用其自己的本地命名空间执行。```python id="4q8zlf"
x = "global"
class Example:
x = "class local"
y = x
print(Example.x)
print(Example.y)
```输出:```text id="7d4l4l"
class local
class local
```类主体中的赋值写入类命名空间,而不是实例。
类内的函数体不会自动捕获类局部名称:```python id="kkxro6"
class Example:
x = 10
def method(self):
return x
```这通常会在运行时失败,除非存在全局`x`,因为方法全局查找使用模块全局变量,而不是类命名空间。
正确的:```python id="mjh0gx"
class Example:
x = 10
def method(self):
return self.x
```或者:```python id="73a9fm"
class Example:
x = 10
def method(self):
return type(self).x
```## 44.10 创建类对象
执行类主体后,CPython 调用元类。
对于普通的类,这意味着调用`type`。
从概念上讲:```python id="y5jzez"
User = type("User", bases, namespace)
```致电给`type`创建一个`PyTypeObject`内部。
生成的类对象存储:```text id="q6gq90"
class name
base classes
method resolution order
class dictionary
type flags
slot tables
weakref support
instance layout
descriptor information
subclass relationships
```因此,用户定义的类是类型对象。
## 44.11`type(name, bases, namespace)`您可以使用手动创建类`type`。```python id="swqmpu"
def hello(self):
return "hello"
User = type("User", (), {"hello": hello})
u = User()
print(u.hello())
```这在精神上等同于:```python id="owh4rx"
class User:
def hello(self):
return "hello"
```这`class`语句是结构化类创建协议的语法。
## 44.12 元类`__new__`元类可以通过重写来自定义类创建`__new__`。```python id="55v7xr"
class Meta(type):
def __new__(mcls, name, bases, namespace, **kwargs):
print("creating", name)
return super().__new__(mcls, name, bases, namespace)
class User(metaclass=Meta):
pass
```输出:```text id="xsdbla"
creating User
__new__在类对象存在之前接收类名、基类和命名空间。
使用__new__当您需要在创建之前更改类时:text id="e80nxe" modify namespace validate definitions inject methods collect metadata change base classes control class object allocation ## 44.13 元类__init__元类可以通过重写来自定义类初始化__init__。```python id="o0xfzo"
class Meta(type):
def init(cls, name, bases, namespace, **kwargs):
print("initializing", name)
super().init(name, bases, namespace)
class User(metaclass=Meta): pass
`__init__`接收已经创建的类对象作为`cls`。
使用元类`__init__`当您需要在创建后注册或检查类时:```text id="u1q6kk"
register subclasses
validate final class
attach metadata
update external registries
```## 44.14 元类`__call__`调用一个类是由它的元类控制的。
对于普通班级:```python id="kq4rc6"
u = User("Ada")
```由以下人员处理:```text id="y1lk7w"
type(User).__call__(User, "Ada")
```对于普通元类,`type.__call__`执行:```text id="vxizzk"
1. call User.__new__(User, ...)
2. if result is an instance of User, call User.__init__(instance, ...)
3. return instance
```元类可以重写`__call__`:
```python id="ce2m4g"
class SingletonMeta(type):
def __call__(cls, *args, **kwargs):
if not hasattr(cls, "_instance"):
cls._instance = super().__call__(*args, **kwargs)
return cls._instance
class Config(metaclass=SingletonMeta):
pass
```现在每`Config()`调用返回相同的对象。
谨慎使用这个。元类`__call__`全局更改类的实例创建。
## 44.15 实例创建
对于普通班级:```python id="6kcvu5"
class User:
def __new__(cls, name):
print("__new__")
return super().__new__(cls)
def __init__(self, name):
print("__init__")
self.name = name
u = User("Ada")
```输出:```text id="868s0q"
__new__
__init__
__new__创建对象。__init__初始化对象。__new__是一个类似静态的构造函数。它接收类并返回一个对象。__init__接收创建的实例并应返回None。
44.16 类字典
类通过以下方式公开其名称空间__dict__。```python id="zk9rbi"
class User:
kind = "human"
def hello(self):
return "hello"
print(User.dict)
`User.__dict__`通常是只读映射代理。```python id="pj2w2d"
print(type(User.__dict__))
```您不能直接分配到映射代理中:```python id="sgmmu2"
User.__dict__["x"] = 1
```但是您可以在类上分配属性:```python id="4utuvq"
User.x = 1
```这通过类型机制更新底层类字典。
## 44.17 类变量
类变量存储在类对象上。```python id="hkk4e1"
class Counter:
count = 0
```通过类访问:```python id="kts1o0"
print(Counter.count)
```通过实例访问:```python id="8w340h"
c = Counter()
print(c.count)
```如果实例没有`count`,查找在类中找到它。
通过实例赋值创建或更新实例属性:```python id="d1dq8x"
c.count = 10
print(c.__dict__)
print(Counter.count)
```输出:```text id="frwv5a"
{'count': 10}
0
```这是可变类变量错误的常见来源。
## 44.18 可变类变量陷阱```python id="x8s3te"
class Bag:
items = []
def add(self, item):
self.items.append(item)
```用法:```python id="81dg5u"
a = Bag()
b = Bag()
a.add("x")
print(b.items)
```输出:```text id="kqxpp6"
['x']
```两个实例共享相同的类级别列表。
正确的设计:```python id="l9yqqx"
class Bag:
def __init__(self):
self.items = []
def add(self, item):
self.items.append(item)
```将类变量用于共享常量或有意共享状态。对每个实例的状态使用实例变量。
## 44.19 实例字典
普通的用户定义对象都有一个实例字典。```python id="8j4hxd"
class User:
pass
u = User()
u.name = "Ada"
print(u.__dict__)
```输出:```text id="e5beij"
{'name': 'Ada'}
```属性分配将值存储在实例字典中,除非数据描述符拦截该分配。
这就是为什么 Python 对象默认是灵活的。可以动态添加新属性。
## 44.20`__slots__`和实例布局
一个类可以定义`__slots__`使用固定属性槽而不是普通实例字典。```python id="ok0zm7"
class Point:
__slots__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y
```实例`Point`店铺`x`和`y`在固定槽位中。```python id="3bbvk2"
p = Point(1, 2)
print(hasattr(p, "__dict__"))
```通常:```text id="mk5rwc"
False
```槽在类上创建描述符:```python id="1l1jy7"
print(Point.__dict__["x"])
print(Point.__dict__["y"])
```这些描述符读取和写入固定的存储位置。
## 44.21 继承
一个类可以继承一个或多个基类。```python id="9jq7to"
class Animal:
def speak(self):
return "..."
class Dog(Animal):
def speak(self):
return "woof"
```子类将其基类存储在`__bases__`。```python id="s4yqfg"
print(Dog.__bases__)
```方法解析顺序存储在`__mro__`。```python id="zfn7t5"
print(Dog.__mro__)
```属性查找遵循 MRO。
## 44.22 方法解析顺序
对于:```python id="z3o721"
class A:
def f(self):
return "A"
class B(A):
pass
class C(B):
pass
C().f()发现f在A通过 MRO。python id="km9ffo" print(C.__mro__) 输出形状:```text id="mvct9m"
(<class 'main.C'>, <class 'main.B'>, <class 'main.A'>, <class 'object'>)
## 44.23 多重继承
Python 支持多重继承。```python id="vr4scb"
class A:
def f(self):
return "A"
class B:
def f(self):
return "B"
class C(A, B):
pass
print(C().f())
print(C.__mro__)
```根据 C3 MRO 规则,最左边的基地通常具有优先权。
输出:```text id="njnz6d"
A
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
```多重继承很强大,但当类共享方法和初始化路径时,需要协作设计。
## 44.24 合作`super`
`super()`遵循 MRO。```python id="xgd0er"
class A:
def f(self):
return "A"
class B(A):
def f(self):
return "B" + super().f()
class C(A):
def f(self):
return "C" + super().f()
class D(B, C):
def f(self):
return "D" + super().f()
print(D().f())
print(D.__mro__)
```输出:```text id="f6z3iu"
DBCA
super()并不意味着“打电话给我的父母”。它的意思是“在当前课程结束后继续 MRO”。
这在多重继承中至关重要。
44.25 类装饰器
类装饰器在创建后接收类对象。```python id="6z5a86" def register(cls): registry[cls.name] = cls return cls
registry = {}
@register
class User:
pass
这大致是:python id="alod77"
class User:
pass
User = register(User)
## 44.26`__init_subclass__`基类可以定义`__init_subclass__`当代码被子类化时运行它。```python id="zfxjod"
class Model:
registry = []
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
Model.registry.append(cls)
class User(Model):
pass
class Post(Model):
pass
print(Model.registry)
```这通常比用于子类注册的元类更简单。
使用`__init_subclass__`为了:```text id="yp6gp0"
subclass registration
subclass validation
default subclass configuration
lightweight framework hooks
```## 44.27`__set_name__`在类创建期间
创建类后,CPython 调用`__set_name__`在类命名空间中的描述符上。```python id="5vzb6d"
class Field:
def __set_name__(self, owner, name):
print(owner, name)
class User:
id = Field()
name = Field()
```输出形状:```text id="vzb7xd"
<class '__main__.User'> id
<class '__main__.User'> name
```这使得描述符可以发现它们被分配的类和属性名称。
顺序是:```text id="4ow51f"
class body executes
class object is created
descriptor __set_name__ hooks run
__init_subclass__ hooks run on bases
class decorators run
class name is bound
```## 44.28 元类 vs 类装饰器 vs`__init_subclass__`|机制|运行|最适合 |
|---|---|---|
|元类 |班级创建期间 |对类对象创建的深度控制 |
|类装饰器 |课后创作|一次性转型或注册|
|`__init_subclass__`| When subclass is created | Base-class-driven subclass hooks |
|描述符`__set_name__`|课堂结业期间 |字段名称发现 |
更喜欢解决问题的最简单机制。
对于大多数代码:```text id="wd9e73"
class decorator > __init_subclass__ > metaclass
```当您需要自定义命名空间准备、类分配、元类级方法、类调用或跨类层次结构强制执行规则时,元类是合理的。
## 44.29 元类示例:强制执行必需的属性```python id="p13a12"
class RequireTableName(type):
def __new__(mcls, name, bases, namespace):
cls = super().__new__(mcls, name, bases, namespace)
if bases and not hasattr(cls, "table_name"):
raise TypeError(f"{name} must define table_name")
return cls
class Model(metaclass=RequireTableName):
pass
class User(Model):
table_name = "users"
```该元类验证子类。
这也可以通过以下方式实现`__init_subclass__`,这通常更简单:```python id="8wx9md"
class Model:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
if not hasattr(cls, "table_name"):
raise TypeError(f"{cls.__name__} must define table_name")
```除非元类提供了具体的优势,否则请使用基类挂钩。
## 44.30 元类示例:注册表```python id="q07d17"
class RegistryMeta(type):
registry = {}
def __init__(cls, name, bases, namespace):
super().__init__(name, bases, namespace)
if name != "Base":
RegistryMeta.registry[name] = cls
class Base(metaclass=RegistryMeta):
pass
class User(Base):
pass
class Post(Base):
pass
print(RegistryMeta.registry)
```这会在子类创建时记录它们。
再次,`__init_subclass__`可能就足够了:```python id="byf0x9"
class Base:
registry = {}
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
Base.registry[cls.__name__] = cls
```元类应该在简化系统时使用,而不是仅仅在感觉功能强大时使用。
## 44.31 元类示例:自定义命名空间
元类可以通过返回自定义命名空间来拒绝重复的属性名称`__prepare__`。```python id="9r9h1q"
class NoDuplicateDict(dict):
def __setitem__(self, key, value):
if key in self:
raise TypeError(f"duplicate name: {key}")
super().__setitem__(key, value)
class NoDuplicateMeta(type):
@classmethod
def __prepare__(mcls, name, bases):
return NoDuplicateDict()
class Example(metaclass=NoDuplicateMeta):
x = 1
y = 2
```如果`x`被分配两次,类创建将失败。
这是元类适用的一种情况,因为只有元类可以在执行之前自定义类主体命名空间。
## 44.32 内置类和堆类型
CPython 既有静态定义的内置类型,也有动态分配的堆类型。
内置类型的示例:```python id="bsov46"
int
str
list
dict
tuple
type
object
```许多内置类型都是在 C 中定义的。
用户定义的类是在运行时创建的堆类型。
两者都参与相同的对象模型:```python id="fbhr47"
print(type(list))
print(type(object))
print(type(type))
```输出:```text id="errhki"
<class 'type'>
<class 'type'>
<class 'type'>
```即使内置类也是对象。
## 44.33`object`和`type`之间的关系`object`和`type`是中心。```python id="ls93k5"
print(isinstance(object, type))
print(isinstance(type, object))
print(type(type))
print(type(object))
```重要事实:```text id="9186px"
object is the base class of most Python objects
type is the default metaclass of classes
type is an instance of itself
object is an instance of type
type inherits from object
```这个圆形的结构在 CPython 中被小心地引导。
它允许类成为对象,同时仍然具有根继承层次结构。
## 44.34 CPython 类型对象
在 C 级别,类对象由类型对象表示,通常`PyTypeObject`。
类型对象包含:```text id="pj1kt2"
object header
type name
basic instance size
item size for variable-sized objects
method table
slot functions
base classes
MRO
dictionary
flags
allocation functions
deallocation functions
attribute access functions
call behavior
numeric, sequence, and mapping operation tables
```这就是类控制对象行为的原因。类对象包含运行时使用的函数指针和元数据。
## 44.35 类型槽
Python 特殊方法通常对应于 C 级槽。
示例:
| Python方法|运行时操作 |
|---|---|
|`__len__`|长度操作 |
|`__getitem__`|索引 |
|`__setitem__`|项目分配|
|`__iter__`|迭代|
|`__next__`|迭代器下一个|
|`__call__`|致电 |
|`__add__`|数字加法 |
|`__getattribute__`|属性查找 |
|`__new__`|分配|
|`__init__`|初始化 |
创建类时,CPython 通过特殊方法构建槽表。
这使得字节码和 C API 可以高效地调用操作,而无需每次都执行正常的 Python 属性查找。
## 44.36 特殊方法查找
特殊方法查找通常会绕过实例字典。
例子:```python id="w8gu52"
class Example:
pass
e = Example()
e.__len__ = lambda: 10
len(e)
```这引发了`TypeError`, 因为`len(e)`寻找`__len__`在类型上,而不是作为任意实例属性。
正确的:```python id="a41p2c"
class Example:
def __len__(self):
return 10
e = Example()
print(len(e))
```特殊方法属于该类,因此 CPython 可以填充和使用类型槽。
## 44.37 类的属性查找
例如访问:```python id="ars6ez"
obj.attr
```CPython 搜索实例及其类层次结构。
对于类访问:```python id="e6vx8d"
Class.attr
```CPython 搜索类对象及其元类。
例子:```python id="511n1a"
class Meta(type):
label = "meta"
class User(metaclass=Meta):
label = "class"
print(User.label)
```输出:```text id="8dznft"
class
```如果`label`缺席于`User`,查找可能会找到它`Meta`。
元类定义类对象的行为和属性。
## 44.38 元类方法
元类方法是类对象上的方法。```python id="7avylj"
class Meta(type):
def describe(cls):
return cls.__name__
class User(metaclass=Meta):
pass
print(User.describe())
```这里,`describe`被发现于`Meta`并绑定到`User`。
这类似于如何在`User`并绑定到`user_instance`。```text id="dm9zxe"
instance method:
User.method -> bound to instance
metaclass method:
Meta.method -> bound to class object
```## 44.39 类和描述符
类字典存储描述符。```python id="jcw8ig"
class User:
@property
def name(self):
return "Ada"
print(User.__dict__["name"])
```当实例访问时`name`,描述符逻辑运行。```python id="bochjq"
u = User()
print(u.name)
```方法也是描述符:```python id="yrosvm"
class User:
def hello(self):
return "hello"
u = User()
print(u.hello)
```该函数存储在`User.__dict__`绑定到`u`通过它的描述符`__get__`。
## 44.40 类和垃圾收集
类对象可以参与引用循环。
示例:```text id="zbbqva"
class object references methods
methods reference globals
functions reference code objects
closures may reference class-related state
instances reference class
class may reference descriptors
descriptors may reference owner class
```CPython 的循环垃圾收集器可以收集无法访问的类和相关对象(如果它们未被其他引用保持活动状态)。
大多数普通类一直存在到进程退出为止,因为模块保留对它们的引用。```python id="25zzxi"
class User:
pass
```模块字典保存`User`。
## 44.41 类和模块
类记录了定义它的模块。```python id="23z7po"
class User:
pass
print(User.__module__)
```通常:```text id="btn8n6"
__main__
```或模块名称。
这会影响表示、酸洗、文档和内省。
类在物理上并不属于模块。模块字典仅保存对类对象的引用。```python id="vxme1s"
OtherName = User
```现在,同一个类对象有另一个名称。
## 44.42 类标识
类标识就是对象标识。```python id="05l12d"
class User:
pass
A = User
B = User
print(A is B)
```输出:```text id="krnpze"
True
```如果一个模块以不同的名称导入两次,类对象可能会重复。```text id="59akdg"
package.models.User
models.User
```即使它们来自同一文件,它们也可能是不同的类对象。
那打破了`isinstance`、注册表查找、序列化和单例假设。
## 44.43 类注释
类注解存储在`__annotations__`。```python id="qmsv40"
class User:
id: int
name: str = "anonymous"
print(User.__annotations__)
```输出:```text id="fzxf79"
{'id': <class 'int'>, 'name': <class 'str'>}
```注释不会自动创建实例字段。```python id="pveplo"
u = User()
print(hasattr(u, "id"))
```通常:```text id="lxeohq"
False
```数据类、attrs、Pydantic 和 ORM 等框架检查注释以生成行为。
## 44.44 数据类作为类转换`dataclasses.dataclass`是一个类装饰器。```python id="r4xqnl"
from dataclasses import dataclass
@dataclass
class User:
id: int
name: str
```它接收类对象、检查注释并添加方法,例如:```text id="i9m60v"
__init__
__repr__
__eq__
```这表明可以在类创建后添加主要的类行为,而无需元类。
## 44.45 抽象基类
的`abc`模块使用元类机制。```python id="cudwvv"
from abc import ABC, abstractmethod
class Store(ABC):
@abstractmethod
def get(self, key):
pass
ABC用途ABCMeta,一个跟踪抽象方法并在实现之前阻止实例化的元类。```python id="j5b315"
class BadStore(Store):
pass
BadStore()
```这引发了TypeError。
抽象基类是元类强制执行类契约的一个实际例子。
44.46 类和isinstance
isinstance(obj, cls)通常检查是否type(obj)是cls或一个子类cls。```python id="3almnz"
class Animal:
pass
class Dog(Animal): pass
d = Dog()
print(isinstance(d, Dog))
print(isinstance(d, Animal))
```元类可以通过以下方式自定义它__instancecheck__。
ABC 机器将其用于虚拟子类行为。
44.47 类和issubclass
issubclass(A, B)检查是否类A是类的子类B。```python id="3tkb0g"
class A:
pass
class B(A): pass
print(issubclass(B, A))
```元类可以通过以下方式自定义它__subclasscheck__。
这是类行为由元类逻辑调节的另一个地方。
44.48 动态类创建
动态类创建在框架和代码生成中很有用。```python id="pq2ye4" def make_model(name, fields): namespace = {"annotations": fields} return type(name, (), namespace)
User = make_model("User", {"id": int, "name": str})
print(User)
print(User.annotations)
动态类仍应遵循普通类规则:text id="kwqh7u"
valid name
clear module
stable identity
predictable bases
explicit public API
放`__module__`需要时:python id="hyvbmw"
namespace = {
"module": name,
"annotations": fields,
}
## 44.49 何时使用元类
当您需要其中之一时,请使用元类:```text id="j2iimq"
custom class namespace through __prepare__
control over class allocation through __new__
metaclass-level methods or properties
custom class call behavior
deep integration across a class hierarchy
framework-level class validation
special instance or subclass checks
```当类装饰器或`__init_subclass__`就足够了。
元类对于类层次结构来说是全局的。当不相关的库定义不同的元类时,它们的组合很差。
## 44.50 要点
类是运行时对象。
大多数类都是实例`type`。
元类是类的类型。
类定义执行主体、收集命名空间并调用元类来创建类对象。
实例是通过调用类来创建的,该类由元类处理`__call__`。
类字典存储方法、描述符、注释和类变量。
继承使用方法解析顺序。
特殊方法被安装到类型槽中,并且通常在类上查找,而不是在实例上查找。
描述符,`__set_name__`, `__init_subclass__`、类装饰器和元类都参与类构造。
仅当较简单的类机制无法表达所需的行为时才使用元类。