每个对象都是由另一个特殊对象创建出来的。 这些特殊对象就是类(class), 而你通过配置类,让它们产生你想要的对象。

类本身也是对象,而对象是可以修改的。 这里的代码清单使用 display_object(), 它是本章在构建 display_object()一节中实现的 inspection helper:

# modify_class.py
from display import display_object

class Clay:
    pass

display_object(Clay)
#: [Attributes]
#:   None
#: [Methods]
#:   None

x = Clay()
display_object(x)
#: [Attributes]
#:   None
#: [Methods]
#:   None

Clay.n = 42  # type: ignore
display_object(Clay)
#: [Attributes]
#:   • n = 42 [CV]
#: [Methods]
#:   None

Clay.m = lambda self: f"{self.n = }"  # type: ignore
display_object(Clay)
#: [Attributes]
#:   • n = 42 [CV]
#: [Methods]
#:   • m(self)

print(x.m())  # type: ignore
#: self.n = 42

display_object(x)
#: [Attributes]
#:   • n = 42 [CV]
#: [Methods]
#:   • m(self)

print(vars(x))
#: {}

即使是在 x 创建之后,你对 class 所做的修改,x 仍然能看到。 instance 本身并没有发生变化。 最后一行表明,它自己的 instance dictionary 仍然是空的。 对 instance 做 attribute lookup 时,如果 instance 上找不到,就会继续到它的 class 上查找, 因此对 class 的一次修改会影响该 class 的所有对象, 包括那些早已创建出来的对象。

那么,这些“class”对象又是谁创建的? 答案是另一类特殊对象,叫作元类(metaclass)。 默认 metaclass 是 type,而绝大多数情况下,它做的事情都正合适。 你可以自定义 Python 创建 class 的方式:在构建每个 class 时运行额外代码,或者向其中注入成员。 这就是 metaclass programming。

其实你已经使用过 metaclass,只是没有亲手写过。 abc.ABCMeta 会构建 abc.ABC (重新思考对象会实际使用它), 并让一个仍有 abstract method 未实现的 class 拒绝实例化。 enum.EnumType 会构建每个 Enum subclass, 把 class body 中的每个 assignment 都转换为一个 member,并让 for c in Color 能遍历这些 member。 遍历一个 class,属于 class object 自身的行为; metaclass 可以把这种行为放到 class object 上,而普通 class 做不到。

你很少真正需要 metaclass。 它是一种非常有意思、也很容易让人产生使用冲动的工具, 但更简单的 hook 已经覆盖了过去 metaclass 才能处理的几乎所有场景:

只有当这些办法都无法完成任务时,才使用 metaclass。 本章首先通过手工构建 class,展示一条 class 语句实际上做了什么。 接着介绍这些更简单的 hook,然后再讨论那些确实仍需要 metaclass 的场景。 最后用 inspect module 从另一个方向收尾: 读取 class 的结构,而不是修改它。

使用 type 生成类

既然 metaclass 用来创建 class,你也可以自己直接调用 metaclass。 type 接收一个参数时,会返回已有对象的类型。 type 接收三个参数时,会创建一个新的 class。 这三个参数分别是名称、base class tuple, 以及包含 field 和 method 的 namespace dictionary。 class definition 只是调用 type 的一种简写:

# class_via_type.py
class C:
    pass

D = type("D", (), {})  # The same construction, by hand

print(type(C), type(D))
#: <class 'type'> <class 'type'>
# Both inherit object:
print(C.__bases__, D.__bases__)
#: (<class 'object'>,) (<class 'object'>,)
# Both make ordinary instances:
print(isinstance(C(), C), isinstance(D(), D))
#: True True

你也可以用同样的方式添加 base class、field 和 method:

# my_list.py
from display import display_object

def howdy(self, you: str) -> None:
    print(f"Howdy,{you}")

MyList = type("MyList", (list,), dict(x=42, howdy=howdy))

display_object(MyList)
#: [Attributes]
#:   • x = 42 [CV]
#: [Methods]
#:   • append(self, object, /)
#:   • clear(self, /)
#:   • copy(self, /)
#:   • count(self, value, /)
#:   • extend(self, iterable, /)
#:   • howdy(self, you: str) -> None
#:   • index(self, value, start=0, stop=92233720368547758...
#:   • insert(self, index, object, /)
#:   • pop(self, index=-1, /)
#:   • remove(self, value, /)
#:   • reverse(self, /)
#:   • sort(self, /, *, key=None, reverse=False)

ml = MyList()
ml.append("Camembert")
print(ml)
#: ['Camembert']
print(ml.x)
#: 42
ml.howdy("John")
#: Howdy, John

print(ml.__class__.__class__)
#: <class 'type'>

因为 MyList 继承自 list,所以它获得了 list 的所有 method。

打印 class 的 class,得到的就是 metaclass。