元类

在面向对象程序设计中，元类（metaclass）是一种实例是类的类。普通的类定义的是特定对象的行为，元类定义的则是特定的类及其实例的行为。不是所有面向对象编程语言都支持元类。在它能做的事情之中，元类可以覆写任何给定方面类行为的程度是不同的。元类可以通过使类成为头等对象来实现，在这种情况下元类简单的就是构造类的一个对象。每个语言都有它自己的元对象协议，给出对象、类和元类如何交互的规则。

## Smalltalk-80元类

在Smalltalk中，所有东西都是对象。此外，Smalltalk是基于类的系统，这意味着所有对象都有一个类，它定义这个对象的结构（比如说这个类拥有实例变量），和这个对象所理解的消息。二者在一起蕴含了，在Smalltalk中，类是一个对象，因此类也需要是它的元类的实例。

元类在Smalltalk-80系统中的主要角色，是提供协议来初始化类变量，和建立元类的唯一实例（也就是其对应的类）的初始化实例。

### 实例联系

为了允许类拥有它们自己的方法，和叫作类实例变量它们自己的实例变量，Smalltalk-80为每个类`C`介入了它们自己的元类`C class`。就像实例方法实际上属于类一样，类方法实际上属于元类。在类中定义实例变量和类变量，而在元类中定义类实例变量。

每个元类在效果上都是单例类。就像连体双胞胎，类和元类是共生的。元类有一个实例变量`thisClass`，它指向它结合的类。平常的Smalltalk类浏览器，不将元类展示为单独的类，转而允许一起同时编辑类和它的元类。

要得到一个实例的类，需要向它发送消息调用`class`方法。类和元类继承了其超类的`name`方法，它返回接收者名字的字符串。例如，轿车对象`c`是类`Car`的实例，则`c class`返回`Car`类对象，而`c class name`返回`'Car'`；依次类推，`Car class`返回`Car`的元类对象，而`Car class name`返回依赖于实现，有的是`nil`，即没有名字，有的是`'Car class'`，即用空格分隔的类名字和`'class'`。

在早期的Smalltalk-76中，创建新类的方式是向`Class`类发送`new`消息。在Smalltalk-80中，`Class`是元类的基础类，它是类而不是元类。所有元类都是一个`Metaclass`类的实例。`Metaclass`类是`Metaclass class`的实例，而`Metaclass class`作为元类，也是`Metaclass`类的实例。

### 继承联系

在Smalltalk-80中，终端对象是一个整数、一个组件、或一台车等，而类是像`Integer`、或`Widget`或`Car`等这样的东西，除了`Object`之外，所有的类都有一个超类。元类所继承的元类，就是元类对应的类所继承的类的元类。

在一个消息被发送到对象的时候，方法的查找开始于它的类。如果没有找到则在上行超类链，停止于`Object`而不管找到与否。在一个消息被发送到一个类的时候，类方法查找开始于它的元类，并上行超类链至`Object class`。直至`Object class`，元类的超类层级并行于类的超类层级。在Smalltalk-80中，`Object class`是`Class`的子类：

```smalltalk
Object class superclass == Class.
```

类方法的查找在元类链之后仍可继续下去，所有元类都是`Class`的在继承层级中的子类，它是所有元类的抽象超类，它描述这些类的一般性质，继而最终可上溯至`Object`。

### 继承层级

四个类提供描述新类的设施，下面是它们的继承层级（起自`Object`），和它们提供的主要设施：

- `Object`，对象类是所有类的基础类，它为所有对象提供公共的方法，即公共的缺省行为。至少包括了：测试对象的功能比如`class`方法，比较对象，对象复制，访问对象的各部分，打印和存储对象，错误处理。
- `Behavior`，行为类定义了拥有实例的对象所需要的最小状态 (计算机科学)。`Behavior`提供建立一个类的实例的`new`方法，包括了一个类层级连接（`superclass:`），一个方法字典（`methodDictionary:`、`addSelector:withMethod:`等），和对实例的描述（`allInstances`、`instVarNames`等）。`Behavior`还为Smalltalk解释器提供到编译器的基本接口来编译方法源代码（`compile:`等）。尽管一个类的多数设施都规定在`Behavior`中，但很多消息不能于此实现，对类的完全描述转而在它的子类之中提供。
- `ClassDescription`，类描述类为`Class`和`Metactass`提供了共同的基础类。它表现为：类命名（`name`）、类注释（`comment`和`comment:`）、和命名实例变量（`addlnstVarName:`等）。特别是，它增加了结构来组织在方法字典中方法（`compile:classified:`）和类自身（`category:`）。`ClassDescription`还提供了在外部串流（文件）上存储完全的类描述的机制，和记述对类描述的变更的机制。
- `Metaclass`，元类类是创建元类的类，它为所有元类提供公共的方法。`Metaclass`增加了关键性消息，一个是发送到`Metaclass`自身的消息`subclassOf: superMeta`，用来创建元类`superMeta`的一个子类；另一个是发送到`Metaclass`某个实例的消息，用来建立这个元类的唯一实例，并对这个类进行完全的初始化，它的一系列参数中针对类和超类名字的参数是：`name:……subclassOf:……`。
- `Class`，类类是所有元类的基础类，它为所有类提供公共的方法。`Class`提供比`ClassDescription`更具描述性的设施，尤其是增加了对类变量名字（`addClassVarName:`等）和共享的池变量（`addSharedPool:`等）的表示，并定义了预期在子类中覆写的方法`initialize`，即在需要时定义在元类中的类方法`initialize`来初始化类变量。`Class`还提供比`Behavior`更综合性的编程支持设施，比如创建一个类的子类的消息：`subclass:instanceVariableNames:classVariableNames:poolDictionaries:category:`。

### 例子

下列例子展示，从Smalltalk-80派生的Squeak和Pharo的样例代码的结构，它们的继承层级的根类实际上是`ProtoObject`，`ProtoObject`封装了所有对象都必须拥有的极小化的消息集合，它被设计为引发尽可能多的错误，用来支持代理（proxy）定义。例如Smalltalk-80的`Object`中，错误处理消息`doesNotUnderstand:`，和系统原始消息`become:`，就转而在`ProtoObject`中定义了。

在示意图中，纵向的绿色连接，展示继承联系的“子→父”关系（隐含的自下而上），横向的蓝色连接展示实例联系的“成员→容器”关系，从`x`出的发蓝色连接，指向`x`的最小实际容器，它是在调用在`x`上的方法时查找方法的继承链起点：

<image>

```squeak
 r := ProtoObject.
 c := Class.
mc := Metaclass.
Object subclass: #A.
A      subclass: #B.
u := B new.
v := B new.
```

这个结构由两个部分构成，用户部分有四个显式的对象和类及其两个隐式的元类：终端对象`u`和`v`，它们连接到的类`A`和`B`，它们两个连接到的右侧灰色节点表示的隐式的元类，其他的对象都是内建部分。

### 方法查找次序

下面是方法查找次序的辨析：

- 每个终端对象，在查找方法时，都首先查找自己的类；然后按类继承链上溯，最终直接上溯至`Object`（对象类）。
- 每个类，包括`Object`（对象类）、`Class`（类类）和`Metaclass`（元类类），在查找查找方法时，首先查找自己的元类；然后按元类继承链上溯，最终经过`Object class`（对象元类）而上溯至`Class`（类类）；接着按类继承链上溯`ClassDescription`（类描述类），最终经过`Behavior`（行为类）上溯至`Object`（对象类）。
- 每个元类，包括`Metaclass class`（元类元类），在查找方法时，首先查找`Metaclass`（元类类）；然后按类继承链上溯`ClassDescription`（类描述类），最终经过`Behavior`（行为类）上溯至`Object`（对象类）。

### 类图示意

下面是两个示意图，二者都是纵向连线表示实例联系，而横向连线表示继承联系。实例联系以`Metaclass`（元类类）及其元类为顶端，而继承联系以`Object`（对象类）及其元类为中心，其中`Object class`（对象元类）继承`Class`（类类）是串接元类继承链与类继承链的关键环节。前者图示将`Metaclass`及其元类放置在最上方的独立两行，使得实例联系尽量成为树状向上汇聚；后者图示将`Metaclass`及其元类放置在最左边，使得继承联系尽量都在同一行之上。

<image>

- Smalltalk-80的类和元类层级，使用UML类图表示，这里从左至右，第一列是Class class（类元类）和Class（类类），第二列是ClassDescription class（类描述元类）和ClassDescription（类描述类），第三列是Behavior class（行为元类）和Behavior（行为类），第四列是Object class（对象元类）和Object（对象类），第五列是Metaclass class（元类元类）、Metaclass（元类类）、Car class（Car元类）、Car（Car类）和c:Car（Car实例）。

<image>

- Smalltalk中在类和元类之间的继承和实例联系的示意图，这里从左至右，第一列是Metaclass元类和Metaclass（元类类），第二列是Class元类和Class（类类），第三列是ClassDescription元类与Behavior元类、和ClassDescription（类描述类）与Behavior（行为类），第四列是Object元类、Object（对象类）和Object实例，第五列是Foo元类、Foo类和Foo实例，第六列是Bar元类、Bar类和Bar实例。

## Objective-C元类

在Objective-C中的元类，几乎同于Smalltalk-80的元类，这是因为Objective-C从Smalltalk引进了很多东西。就像Smalltalk，在Objective-C中实例变量和方法是对象的类定义的。类也是对象，因此它是元类的一个实例。

<image>

- 在Objective-C中在类和元类之间的继承和实例联系的示意图。注意Objective-C有多个根类，每个根类都有独立的层级。这个示意图只展示了例子根类NSObject的层级。每个其他根类都有类似的层级。

就像Smalltalk，在Objective-C中类方法，简单的是在类对象上调用的方法，因此一个类的类方法，必须定义为在它的元类中的实例方法。因为不同的类有不同的类方法集合，每个类都必须有它自己单独的元类。类和元类总是成对创建：运行时系统拥有函数`objc_allocateClassPair()`和`objc_registerClassPair()`来分别的创建和注册类-元类对。

元类没有名字，但是到任何类对象的指针，可以通过泛化类型`Class`来提及（类似于用作到任何对象的指针的类型`id`）。

元类都是相同的类即根类元类的实例，而根类元类是自身的实例。因为类方法是通过继承联系来继承的，就像Smalltalk，除了根类元类之外，元类继承联系必须并行于类继承联系（比如说如果类A的父类是类B，则A的元类的父类是B的元类）。

不同于Smalltalk，根类元类继承自根类自身（通常为使用Cocoa框架的`NSObject`）。这确保了所有的元类最终都是根类的子类，从而人们可以将根类的实例方法，它们通常是针对对象有用的实用方法，使用于类对象自身上。

## Python元类

在Python中，内建的类`type`是元类。Python与Smalltalk-80在对象系统上最显著区别，是Python中类方法的创建由内建的类方法修饰器`classmethod`统一处理，经过修饰的类方法与实例方法以同样的方式存储在类中。由于不需要为每个类建立自己的元类来持有它的类方法，也就无需再特设Smalltalk-80中的元类类`Metaclass`。每个类在创建自身子类时查找构造方法最终上溯到共同的一个类，它在Smalltalk-80中是类类`Class`，而在Python中就是类型类`type`。

### 概述

在Python中可以创建继承自缺省元类`type`的自定义元类，用户可以通过在类定义中提供关键字参数`metaclass`来使用这种自定义元类。例如：

<image>

```python
r = object
c = type

class M(type): pass
class A(metaclass=M): pass
class B(A):
    def f(self): pass

b = B()
```

下面先建立辅助函数，`inspect()`打印作为类的一个对象的元类、基类列表和排序后的特性与方法名字列表，`review()`打印作为类的一个对象的元类和基类列表，`differ()`求得两个对象的字典的差集，`intersect()`求得两个对象的字典的交集，`differ_dir()`求得两个对象上的`__dir__()`的差集，`name_list()`给出一个容器中的所有对象的名字列表，`fullname_list()`给出一个容器中的所有对象的详尽名字列表，`belong()`测试一个对象是否在另一个对象的字典之中：

```python
def inspect(a):
    print(type(a).__name__,  
        [x.__name__ for x in a.__bases__] if hasattr(a, '__bases__') else None,  
        sorted([*a.__dict__]) if hasattr(a, '__dict__') else None)
    
def review(a):
    print(type(a).__name__,  
        [x.__name__ for x in a.__bases__] if hasattr(a, '__bases__') else None)

def differ(x, y):
    return sorted({*x.__dict__} - {*y.__dict__})
    
def intersect(x, y):
    return sorted({*x.__dict__} & {*y.__dict__})

def differ_dir(x, y):
    return sorted({*x.__dir__()} - {*y.__dir__()})

def name_list(a):
    return [x.__name__ for x in a]

def fullname_list(a):
    return [x.__module__+'.'+x.__qualname__ for x in a]

def belong(x, a):
    return x.__name__ in a.__dict__
```

再内省这个例子所建立的类对象与类实例对象及其实例方法，和内建的所有类的基类`object`与元类`type`:

```pycon
>>> inspect(b)
B None []
>>> inspect(B)
M ['A'] ['__doc__', '__firstlineno__', '__module__', '__static_attributes__', 'f']
>>> inspect(b.f)
method None []
>>> assert b.f is not b.f == b.f
>>> inspect(B.f)
function None []
>>> inspect(B.f.__get__)
method-wrapper None None
>>> assert b.f == B.f.__get__(b)
>>> differ_dir(b.f, B.f)
['__func__', '__self__']
>>> assert b.f.__func__ is B.f
>>> assert b.f.__self__ is b
>>> 
>>> inspect(A)
M ['object'] ['__dict__', '__doc__', '__firstlineno__', '__module__', '__static_attributes__', '__weakref__']
>>> differ(A, B)
['__dict__', '__weakref__']
>>> name_list(A.__subclasses__())
['B']
>>> inspect(M)
type ['type'] ['__doc__', '__firstlineno__', '__module__', '__static_attributes__']
>>> 
>>> review(__builtins__)
module None
>>> assert belong(object, __builtins__)
>>> assert belong(type, __builtins__)
>>> review(object)
type []
>>> review(type)
type ['object']
>>> fullname_list(type.__subclasses__(type))
['abc.ABCMeta', 'enum.EnumType', 'ast._ABC', '__main__.M', 'typing._AnyMeta', 'typing.NamedTupleMeta', 'typing._TypedDictMeta']
```

这里的表示了，内建函数`id()`返回一个对象的“标识”，它是保证对于这个对象在其生命时期内唯一并且恒定的一个整数，对于CPython就是这个对象在主存储器中的地址，具有不交叠的生命时期的两个对象可以有相同的`id()`值。Python 3.13版本为自定义类增加了`__firstlineno__`和`__static_attributes__`特性。

### 数据模型

下面继续内省Python的数据模型：

```pycon
>>> differ(object, type)
['__class__', '__eq__', '__format__', '__ge__', '__getstate__', '__gt__', '__hash__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__reduce__', '__reduce_ex__', '__str__', '__subclasshook__']
>>> intersect(object, type)
['__delattr__', '__dir__', '__doc__', '__getattribute__', '__init__', '__new__', '__repr__', '__setattr__', '__sizeof__']
>>> differ(type, object)
['__abstractmethods__', '__annotate__', '__annotations__', '__base__', '__bases__', '__basicsize__', '__call__', '__dict__', '__dictoffset__', '__flags__', '__instancecheck__', '__itemsize__', '__module__', '__mro__', '__name__', '__or__', '__prepare__', '__qualname__', '__ror__', '__subclasscheck__', '__subclasses__', '__text_signature__', '__type_params__', '__weakrefoffset__', 'mro']
```

- `object`字典减除`type`字典的差集中的方法或特性含摄：
- 标准类型类层级中，类实例的特殊特性`__class__`。
- 对象特殊方法中，基本定制（customization）方法：字符串表示方法`__repr__()`，格式化字符串表示方法`__str__()`和`__format__()`，丰富比较方法`__lt__()`、`__le__()`、`__eq__()`、`__ne__()`、`__gt__()`和`__ge__()`，散列运算方法`__hash__()`。
- 对象特殊方法中，定制化（customizing）类创建的类方法`__init_subclass__()`的缺省实现。
- 标准库中，抽象基类（`ABC`）模块的`ABCMeta`元类所创建诸类的方法`__subclasshook__()`的缺省实现。
- 标准库中，封存（`pickle`）模块的进行对象序列化的类实例方法`__getstate__()`、`__reduce__()`和`__reduce_ex__()`的缺省实现。
- `object`字典与`type`字典的交集中的方法或特性含摄：
- 标准类型类层级中，诸对象的特殊特性`__doc__`。
- 对象特殊方法中，基本定制（customization）方法`__init__()`、`__new__()`和`__repr__()`。
- 对象特殊方法中，定制化（customizing）特性访问方法`__getattribute__()`、`__setattr__()`、`__delattr__()`和`__dir__()`。
- 标准库中，`sys.getsizeof()`调用的对象特殊方法`__sizeof__()`。
- `type`字典减除`object`字典的差集中的方法或特性主要含摄：
- 标准类型类层级中，自定义（custom）类的特殊特性`__name__`、`__bases__`、`__mro__`、`__module__`、`__qualname__`，取置描述器`getset_descriptor`类型的`__dict__`属性，以及特殊方法`__subclasses__()`和`mro()`。
- 对象特殊方法中，模拟可调用对象方法`__call__()`的缺省实现。
- 对象特殊方法中，定制化（customizing）实例与子类检查特殊方法`__instancecheck__()`和`__subclasscheck__()`。
- 对象特殊方法中，定制化（customizing）类创建的方法`__prepare__()`的缺省实现。
- 标准库中，抽象基类（`ABC`）模块设立的数据描述器`__abstractmethods__`，这个特殊属性由`ABCMeta`元类在`__new__()`方法中为其实例进行设置。

在`type`字典之中，Python 3.10版本介入了用于联合类型的`__or__(self, other)`和`__ror__(self, other)`方法，它们是模拟数值类型的特殊方法，分别实现了二元运算`|`和交换了运算元的二元运算`|`；Python 3.12版本中增加了`__type_params__`特性，Python 3.14版本更改了`__annotations__`特性并增加了`__annotate__()`方法；还有作为CPython实现细节的数据成员：`__basicsize__`、`__itemsize__`、`__flags__`、`__weakrefoffset__`、`__base__`、`__dictoffset__`，以及给标准库`inspect.signature()`去做分析的`__text_signature__`特性。

### 实例关系与子类关系

使用特殊方法`type.__instancecheck__(self, instance)`和`type.__subclasscheck__(self, subclass)`，分别内省上述实例对象、类和元类之间的实例关系和子类关系：

```pycon
>>> inspect(type.__subclasscheck__)
method_descriptor None None
>>> assert type.__subclasscheck__(type, M) is True
>>>
>>> inspect(type.__subclasscheck__.__call__)
method-wrapper None None
>>> inspect(type.__subclasscheck__.__get__)
method-wrapper None None
>>> assert M.__subclasscheck__.__objclass__ is type
>>> assert M.__subclasscheck__ is type.__subclasscheck__ \
...     != type.__subclasscheck__.__get__(type)
>>> assert object.__subclasscheck__ == type.__subclasscheck__.__get__(object)
>>> assert A.__subclasscheck__ == type.__subclasscheck__.__get__(A)
>>> inspect(object.__subclasscheck__)
builtin_function_or_method None None
>>> inspect(A.__subclasscheck__)
builtin_function_or_method None None
>>> assert object.__subclasscheck__(type) is True
>>> assert object.__subclasscheck__(M) is True
>>> assert object.__subclasscheck__(A) is True
>>> assert object.__subclasscheck__(B) is True
>>> assert A.__subclasscheck__(B) is True+
>>> 
>>> inspect(type.__instancecheck__)
method_descriptor None None
>>> assert type.__instancecheck__(type, object) is True
>>> assert type.__instancecheck__(type, M) is True
>>> assert type.__instancecheck__(M, B) is True
>>> assert type.__instancecheck__(M, A) is True
>>> 
>>> inspect(type.__instancecheck__.__call__)
method-wrapper None None
>>> inspect(type.__instancecheck__.__get__)
method-wrapper None None
>>> assert M.__instancecheck__.__objclass__ is type
>>> assert M.__instancecheck__ is type.__instancecheck__ \
...     != type.__instancecheck__.__get__(type)
>>> assert object.__instancecheck__ == type.__instancecheck__.__get__(object)
>>> assert B.__instancecheck__ == type.__instancecheck__.__get__(B)
>>> inspect(object.__instancecheck__)
builtin_function_or_method None None
>>> inspect(B.__instancecheck__)
builtin_function_or_method None None
>>> assert object.__instancecheck__(b) is True
>>> assert B.__instancecheck__(b) is True
```

方法描述器`method_descriptor`的实例，通过其方法包装器`method-wrapper`类型的`__get__()`方法，接受一个对象从而产生内建方法。描述器的实例可能出现`__objclass__`特性，它由标准库的`inspect`模块解释为指定这个对象在其中定义的那个类；对于具有`__call__()`方法的可调用对象，它可以指示这个给定类型或其子类的一个实例被预期或要求作为其第一个实际参数。由于自定义元类可以覆写元类`type`的`__instancecheck__()`和`__subclasscheck__()`方法，这里尽量不采用元类的方法描述器转而采用它所产生的内建方法。

### 字典与目录

内省特殊特性`__dict__`，它的类型对于类实例对象就是字典`dict`，而对于类对象是映射代理`mappingproxy`：

```pycon
>>> import types
>>> assert type(b.__dict__) is dict
>>> assert type(B.__dict__) is types.MappingProxyType
>>> assert types.MappingProxyType({'x':1})['x'] == 1
>>> assert B.__dict__ is not B.__dict__ == B.__dict__
>>> 
>>> assert type(A.__dict__['__dict__']) is types.GetSetDescriptorType
>>> assert type(type.__dict__['__dict__']) is types.GetSetDescriptorType
>>> inspect(types.GetSetDescriptorType)
type ['object'] ['__delete__', '__doc__', '__get__', '__name__', '__objclass__', '__qualname__', '__repr__', '__set__']
>>> inspect(types.GetSetDescriptorType.__get__)
wrapper_descriptor None None
>>> 
>>> assert type(A.__dict__['__dict__'].__get__) is types.MethodWrapperType
>>> assert b.__dict__ is A.__dict__['__dict__'].__get__(b)
>>> b.__dict__['x'] = 1
>>> assert b.x == 1
>>> 
>>> assert type(type.__dict__['__dict__'].__get__) is types.MethodWrapperType
>>> assert B.__dict__ == type.__dict__['__dict__'].__get__(B)
>>> assert B.f is B.__dict__['f']
```

类实例对象比如这里的对象`b`，在创建它的类`B`的继承链上追溯出作为`object`类的直接子类的基类`A`，它的`__dict__`特性是由这个基类的取置描述器类型的`__dict__`属性产生的；类对象比如这里的类`B`，它的`__dict__`特性是由元类`type`的取置描述器类型的`__dict__`属性产生的。

接着内省特殊方法`object.__dir__()`和`type.__dir__()`以及内建函数`dir()`：

```pycon
>>> inspect(object.__dir__)
method_descriptor None None
>>> assert B.__dir__ is A.__dir__ is object.__dir__
>>> assert b.__dir__ == object.__dir__.__get__(b)
>>> assert b.__dir__() == object.__dir__(b)
>>> def object_dir(a):
...     return (({*a.__dict__} 
...         if not hasattr(a, '__bases__') and hasattr(a, '__dict__')
...         else set()) | {*type.__dir__(a.__class__)})
... 
>>> assert {*object.__dir__(b)} == object_dir(b)
>>> assert {*object.__dir__(B)} \
...     == {*object.__dir__(A)} \
...     == {*type.__dir__(M)}
>>> assert {*object.__dir__(object)} \
...     == {*object.__dir__(M)} \
...     == {*object.__dir__(type)} \
...     == {*type.__dir__(type)}
>>> 
>>> inspect(type.__dir__)
method_descriptor None None
>>> assert M.__dir__ is type.__dir__
>>> assert {*type.__dir__(B)} == {*B.__dict__} | {*type.__dir__(A)}
>>> assert {*type.__dir__(A)} == {*A.__dict__} | {*type.__dir__(object)}
>>> assert {*type.__dir__(M)} == {*M.__dict__} | {*type.__dir__(type)}
>>> assert {*type.__dir__(type)} == {*type.__dict__} | {*type.__dir__(object)}
>>> assert {*type.__dir__(object)} == {*object.__dict__}
>>> 
>>> assert belong(dir, __builtins__)
>>> inspect(dir)
builtin_function_or_method None None
>>> assert dir(b) == sorted(object.__dir__(b))
>>> assert dir(B) == sorted(type.__dir__(B))
>>> differ_dir(dir, type.__dir__)
['__module__', '__self__']
>>> assert dir.__self__ is __builtins__
>>> assert dir.__module__ == 'builtins'
```

这里内建函数的`__self__`和`__module__`特性是实例方法的特殊只读特性。

### 取得特性与弱引用

内省体现了动态分派机制的特殊方法`object.__getattribute__()`和`type.__getattribute__()`：

```pycon
>>> inspect(object.__getattribute__)
wrapper_descriptor None None
>>> inspect(type.__getattribute__)
wrapper_descriptor None None
>>> assert b.__getattribute__ is B.__getattribute__ \
...     is object.__getattribute__ != type.__getattribute__
>>> 
>>> name_list(B.__mro__)
['B', 'A', 'object']
>>> 
>>> assert b.__dict__ is object.__getattribute__(b, '__dict__')
>>> assert B.__dict__ == type.__getattribute__(B, '__dict__')
>>> 
>>> assert b.__dir__ == object.__getattribute__(b, '__dir__')
>>> assert B.__dir__ is A.__dir__ is object.__dir__ \
...     == type.__getattribute__(object, '__dir__')
>>> assert M.__dir__ is type.__dir__ \
...     == type.__getattribute__(type, '__dir__')
```

解释器中所有的类实例对象和类对象的`<small>对象</small>.<small>特性</small>`语法糖，都相当于分别采用`object.__getattribute__()`和`type.__getattribute__()`特殊方法来取得对象的特性。`__getattribute__()`和`__setattr__()`特殊方法的示例，可参见访问者模式的Python表述代码。

一个类的`__mro__`特性确定了它于类层级中的所有基类的优先次序即方法决定次序（MRO），在查找特定的特性或方法之时，次序居前的基类优先于位居其后的它的父辈类或平辈类。比如有一个菱形问题继承层级，类`X`依次多重继承了2个基类`C`和`D`即`class X(C, D): pass`，而类`C`的`__mro__`包含了`[C, A, O]`这3个基类，类`D`的`__mro__`包含了`[D, B, O]`这3个基类，则类`X`的`__mro__`将包含`[C, A, D, B, O]`这5个基类。

接着内省弱引用特性`__weakref__`：

```pycon
>>> import weakref
>>> inspect(A.__weakref__)
getset_descriptor None None
>>> c = weakref.ref(b)
>>> d = weakref.ref(b)
>>> assert d is c is b.__weakref__
>>> inspect(b.__weakref__)
ReferenceType None None
```

在一个类中的类变量`__slots__`，可以被赋值为变量名字序列，它为所声明的这些变量在类实例对象中保留空间，并阻止为其自动建立特性`__dict__`和`__weakref__`。

### 静态方法与类方法

Python中内建的静态方法类`staticmethod`和类方法类`classmethod`，基于了非数据的描述器协议：

```pycon
>>> assert belong(staticmethod, __builtins__)
>>> assert belong(classmethod, __builtins__)
>>> inspect(staticmethod)
type ['object'] ['__call__', '__dict__', '__doc__', '__func__', '__get__', '__init__', '__isabstractmethod__', '__new__', '__repr__', '__wrapped__']
>>> inspect(classmethod)
type ['object'] ['__dict__', '__doc__', '__func__', '__get__', '__init__', '__isabstractmethod__', '__new__', '__repr__', '__wrapped__']
>>> inspect(staticmethod.__call__)
wrapper_descriptor None None
>>> inspect(staticmethod.__get__)
wrapper_descriptor None None
>>> inspect(classmethod.__get__)
wrapper_descriptor None None
```

在方法调用之时，静态方法不接收隐式的第一个实际参数。下面是静态方法描述器的纯Python等价者，它模拟了`cpython/Objects/funcobject.c`中的`PyStaticMethod_Type()`：

```python
import functools

class StaticMethod():
    def __init__(self, f):
        self.f = f
        functools.update_wrapper(self, f)
    def __get__(self, obj, objtype=None):
        return self.f
    def __call__(self, *args, **kwds):
        return self.f(*args, **kwds)
```

这里的`__get__(self, obj)`直接返回所包装的函数`f`，而`__call__(self, *args, **kwds)`以指定实际参数执行所包装的函数`f`。

在方法调用之时，类方法接收这个类的作为其隐式的第一个实际参数。下面是类方法描述器的纯Python等价者，它模拟了`cpython/Objects/funcobject.c`中的`PyClassMethod_Type()`：

```python
import functools
from types import MethodType

class ClassMethod():
    def __init__(self, f):
        self.f = f
        functools.update_wrapper(self, f)
    def __get__(self, obj, cls=None):
        if cls is None:
            cls = type(obj)
        return MethodType(self.f, cls)
```

在Python中，类型和方法可以在运行时动态创建，这里的`__get__(self, obj)`通过方法类型`types.MethodType`，在从类或从实例发起调用而有传入的类引用`cls`的常见情况下，建立将函数`f`绑定到了`cls`上的方法对象；在没有传入的类引用的特殊情况下，建立将函数`f`绑定到了`type(obj)`上的方法对象。

### 数据描述器

通过描述器协议可以建立数据描述器，描述器类定义并指称了参与 (哲学)到其他类之中的，成为其所拥有 (语言学)的非固有的性质。

在一个类的定义中用描述器的构造器来初始化一个特性名字，使其指定 (法律)了这个描述器的一个实例，从而建立了这个类与这个描述器之间的联系（relationship）。数据描述器结合弱引用字典类，用于存储拥有这个描述器的一个类的诸实例所关联数据的示例，可以参见备忘录模式的Python表述代码。

内建的属性类`property`可以建立一个数据描述器，在对拥有一个属性描述器的类实例的一个特性进行访问或变更之时，它触发访问子或变异子函数调用。

```pycon
>>> import types
>>> assert belong(property, __builtins__)
>>> inspect(property)
type ['object'] ['__delete__', '__doc__', '__get__', '__getattribute__', '__init__', '__isabstractmethod__', '__name__', '__new__', '__set__', '__set_name__', 'deleter', 'fdel', 'fget', 'fset', 'getter', 'setter']
>>> assert type(property.__init__) \
...     is type(property.__get__) \
...     is type(property.__set__) \
...     is type(property.__getattribute__) \
...     is types.WrapperDescriptorType
>>> assert type(property.__isabstractmethod__) \
...     is types.GetSetDescriptorType
>>> assert type(property.__set_name__) \
...     is type(property.getter) \
...     is type(property.setter) \
...     is type(property.deleter) \
...     is types.MethodDescriptorType
>>> assert type(property.fget) \
...     is type(property.fset) \
...     is type(property.fdel) \
...     is types.MemberDescriptorType
```

这里的`__set_name__(self, owner, name)`是定制化（customizing）类创建的特殊方法，它在创建拥有者类`owner`之时被自动调用，这个对象在这个类中已经被指派了名字`name`。

下面的例子把属性描述器用作修饰符，将对加以名字修饰的私有变量`x`访问，包装入一个属性描述器：

```pycon
>>> class C():
...     def __init__(self, *args, **kwargs):
...         self.__x = None
...     @property
...     def x(self):
...         """I'm the 'x' property."""
...         return self.__x
...     @x.setter
...     def x(self, value):
...         self.__x = value
...     @x.deleter
...     def x(self):
...         del self.__x
... 
>>> inspect(C)
type ['object'] ['__dict__', '__doc__', '__firstlineno__', '__init__', '__module__', '__static_attributes__', '__weakref__', 'x']
>>> assert type(C.x) is property
>>> assert C.x.__name__ == 'x'
>>> assert C.x.setter == property.setter.__get__(C.x)
>>> assert type(property.__getattribute__(C.x, 'fget')) \
...     is type(property.fget.__get__(C.x)) \
...     is types.FunctionType
>>> c = C()
>>> assert '_C__x' in c.__dict__
>>> assert c.x is C.x.__get__(c)
```

调用描述器实例的非包装描述器类型的方法的示例，还可以参见作为Common Lisp对象系统核心特征的多分派机制的Python模拟表述代码，其中通过采用标准库`functools`中的`singledispatchmethod`，将方法变换为单一分派泛化函数。

下面是属性描述器的纯Python等价者，它模拟了`cpython/Objects/descrobject.c`中的`PyProperty_Type()`：

```python
class Property:
    __slots__ = ('fget', 'fset', 'fdel', '__doc__', '__name__')
    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = doc
    def __set_name__(self, owner, name):
        self.__name__ = name
    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError
        return self.fget(obj)
    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError
        self.fset(obj, value)
    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError
        self.fdel(obj)
    def getter(self, fget):
        return type(self)(fget, self.fset, self.fdel, self.__doc__)
    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel, self.__doc__)
    def deleter(self, fdel):
        return type(self)(self.fget, self.fset, fdel, self.__doc__)
    @property
    def __isabstractmethod__(self):
        return any(getattr(f, '__isabstractmethod__', False)
            for f in (self.fget, self.fset, self.fdel))
```

为了使描述器正确的互操作于抽象基类机制，描述器必须使用`__isabstractmethod__`属性标识自身，一般而言如果组成这个描述器的任何方法是抽象的，则这个属性应当为`True`。这里定义`Property.__isabstractmethod__`属性所用到的属性描述器`property`，模拟的是定义`property.__isabstractmethod__`属性所用到的取置描述器`getset_descriptor`。在前面章节中模拟的静态方法和类方法，也可以类似的用属性描述器`property`来定义`StaticMethod.__isabstractmethod__`和`ClassMethod.__isabstractmethod__`属性。

### 抽象基类

内省作为元类`type`子类的抽象基类元类`ABCMeta`，和抽象基类`ABC`：

```pycon
>>> from abc import ABCMeta, abstractmethod, ABC
>>> review(ABCMeta)
type ['type']
>>> differ(ABCMeta, M)
['__instancecheck__', '__new__', '__subclasscheck__', '_abc_caches_clear', '_abc_registry_clear', '_dump_registry', 'register']
>>> assert type.__subclasscheck__(type, ABCMeta) is True
>>> assert type.__instancecheck__(type, ABCMeta) is True
>>> review(type.__dict__['__abstractmethods__'])
getset_descriptor None
>>> inspect(abstractmethod)
function None []
>>> 
>>> review(ABC)
ABCMeta ['object']
>>> differ(ABC, A)
['__abstractmethods__', '__slots__', '_abc_impl']
>>> ABC.__abstractmethods__
frozenset()
>>> class D(ABC):
...     @abstractmethod
...     def f(self): pass
... 
>>> review(D)
ABCMeta ['ABC']
>>> assert D.f.__isabstractmethod__ is True
>>> D.__abstractmethods__
frozenset({'f'})
```

当指示抽象方法的修饰器`abstractmethod()`同其他方法描述器一起应用之时，它应当被用作最内的修饰符。抽象基类在设计模式中主要用于实现接口。

下面内省内建类型`int`和抽象基类`numbers.Integral`:

```pycon
>>> import numbers
>>> assert belong(int, __builtins__)
>>> review(int)
type ['object']
>>> review(numbers.Integral)
ABCMeta ['Rational']
>>> name_list(numbers.Integral.__mro__)
['Integral', 'Rational', 'Real', 'Complex', 'Number', 'object']
>>> differ(numbers.Integral, int)
['__abstractmethods__', '__firstlineno__', '__module__', '__slots__', '__static_attributes__', '_abc_impl']
>>> 
>>> assert type.__instancecheck__(ABCMeta, numbers.Integral) is True
>>> assert type.__subclasscheck__(numbers.Integral, int) is False
>>> assert type.__instancecheck__(numbers.Integral, 123) is False
>>> assert numbers.Integral.__subclasscheck__ \
...     == ABCMeta.__subclasscheck__.__get__(numbers.Integral)
>>> assert numbers.Integral.__instancecheck__ \
...     == ABCMeta.__instancecheck__.__get__(numbers.Integral)
>>> assert numbers.Integral.__subclasscheck__(int) is True
>>> assert numbers.Integral.__instancecheck__(123) is True
>>> 
>>> assert numbers.Integral.register \
...     == ABCMeta.register.__get__(numbers.Integral)
>>> class MyInt(): pass
...
>>> numbers.Integral.register(MyInt)
<class '__main__.MyInt'>
>>> assert numbers.Integral.__subclasscheck__(MyInt) is True
>>> assert numbers.Integral.__instancecheck__(MyInt()) is True
```

抽象基类元类`ABCMeta`覆写了元类`type`的`__instancecheck__()`和`__subclasscheck__()`方法，并且提供了同抽象基类建立子类关系和实例关系的注册方法`register()`。

### 自定义元类

在一个类中定义`__new__()`方法，它所覆写是其基类的`__new__()`方法，可以定制这个类的实例的创建，比如单例模式和享元模式的Python表述代码。

在一个类中定义`__init__()`方法，用来初始化其实例的特性，下面的例子是一个最简单的类`Car`：

```python
class Car():
    descr = 'Automobile'
    def __init__(self, *args, **kwargs):
        self.descr = kwargs
    def __call__(self, *args, **kwargs):
        self.descr |= kwargs
    @property
    def description(self):
        return (type(self).descr+": "+" ".join(f"{k}={v}"
            for k, v in self.descr.items()))
```

```pycon
>>> car = Car(make='Toyota', model='Prius', year=2005, engine='Hybrid')
>>> car(color='Green')
>>> car.description
'Automobile: make=Toyota model=Prius year=2005 engine=Hybrid color=Green'
```

这种类实例初始化的任务，也可以在自定义（custom）元类的`__call__()`方法中完成：

```python
class AttrInitType(type):
    def __init__(cls, *args):
        cls.descr = 'Automobile'
        def fn(self, *args, **kwargs):
            self.descr |= kwargs
        cls.__call__ = fn
    def __call__(cls, *args, **kwargs):
        obj = type.__call__(cls, *args) 
        obj.descr = kwargs
        return obj

class Car(metaclass=AttrInitType):
    @property
    def description(self):
        return (type(self).descr+": "+" ".join(f"{k}={v}"
            for k, v in self.descr.items()))
```

在自定义元类`AttrInitType`的`__call__(cls, *args, **kwargs)`方法中，直接进行了`type.__call__(cls, *args)`调用，仍采用解释器的语法糖`obj = cls(*args)`会导致无穷递归。元类的方法，将`self`作为函数来调用。这里的形式参数`args`，将会传递给所创建的实例对象的`__init__()`方法，而形式参数`kwargs`被截留于此，在所创建实例对象自身初始化之后对它进行相关的初始设置。

在自定义元类`AttrInitType`的`__init__(self, *args)`方法中，初始化了它所创建的实例类的实例方法和类特性。其形式参数`args`所捕获的元组，包含了传递给作为内建函数的元类构造器的3个唯位置实际参数。元类构造器的实际参数为：

- `name`字符串，是新建类的名字，它会成为这个类的`__name__`特性。
- `bases`元组，包含新建类的诸基类，它会成为这个类的`__bases__`特性。
- `dict`字典，包含新建类的主体中的特性和方法定义，它在成为这个类的`__dict__`特性之前可以被复制或包装。
- `kwargs`字典，包含关键字实际参数，它会被传递给适当的元类机制即通常的`__init_subclass__()`，这种方式同于在类定义中附加除`metaclass`之外的关键字参数。

这个自定义元类也可以写为：

```python
class AttrInitType(type):
    def __new__(mcls, name, bases, dict, /, **kwargs):
        cls = type.__new__(mcls, name, bases, dict)
        cls.descr = kwargs['descr']
        def fn(self, *args, **kwargs):
            self.descr |= kwargs
        cls.__call__ = fn
        return cls
    def __call__(cls, *args, **kwargs):
        obj = type.__call__(cls, *args) 
        obj.descr = kwargs
        return obj

class Car(metaclass=AttrInitType, descr='Automobile'):
    @property
    def description(self):
        return (type(self).descr+": "+" ".join(f"{k}={v}"
            for k, v in self.descr.items()))
```

在自定义元类`AttrInitType`的`__new__()`方法中，直接进行了type.__new__(mcls, name, bases, dict)调用，仍采用解释器的语法糖`cls = mcls(name, bases, dict)`会导致无穷递归。类`Car`还可以写为不常用的等价形式：

```python
def description(self):
    return (type(self).descr+": "+" ".join(f"{k}={v}"
        for k, v in self.descr.items()))
Car = AttrInitType('Car', (), 
    dict(description=property(description)), descr='Automobile')
del description
```

类的初始化也可以在其基类的中用初始化子类方法`__init_subclass__()`来设定：

```python
class AttrInitType(type):
    def __call__(cls, *args, **kwargs):
        obj = type.__call__(cls, *args) 
        obj.descr = kwargs
        return obj

class SubclassInit():
    def __init_subclass__(cls, /, descr, **kwargs):
        super().__init_subclass__(**kwargs)
        cls.descr = descr
        def fn(self, *args, **kwargs):
            self.descr |= kwargs
        cls.__call__ = fn

class Car(SubclassInit, metaclass=AttrInitType, descr='Automobile'):
    @property
    def description(self):
        return (type(self).descr+": "+" ".join(f"{k}={v}"
            for k, v in self.descr.items()))
```

## Ruby元类

Ruby通过介入其自称的本征类（eigenclass），提炼了Smalltalk-80的元类概念，去除了`Metaclass`类，并重新定义了`class-of`映射。变更可以图示如下：

类｜隐式元类 终端对象｜→｜类｜类的本征类｜本征类的本征类 终端对象｜终端对象的本征类

特别要注意在Smalltalk的隐含的元类和Ruby类的本征类之间的对应。Ruby的本征类模型，使得隐式元类概念完全统一：所有对象`x`，都有它自己的元对象，它叫作`x`的本征类，它比`x`高一个元层级。高阶本征类通常是纯粹概念上的存在，在大多数Ruby程序中，它们不包含任何方法也不存储任何（其他）数据。

下面的示意图展示Ruby样例代码的核心结构。这里的灰色节点表示打开`A`和`v`本征类后扩张出来的本征类。

<image>

```ruby
r = BasicObject
c = Class
class A; end
class B < A; end
u = B.new
v = B.new

class << A; end
class << v; end
```

图示还展示了Ruby中本征类的惰性求值。`v`对象可以有它的本征类，作为向`v`增加“单例方法”的结果而被求值（被分配）。

## 在语言和工具中的支持

下面是支持元类的一些最显著的编程语言。

- Common Lisp，通过CLOS
- Delphi和受它影响的其他Object Pascal版本
- Groovy
- Objective-C
- Python
- Perl，通过元类pragma，还有Moose
- Ruby
- Smalltalk
- C++有关提议

一些不甚广泛传播的语言支持元类，包括OpenJava、OpenC++、OpenAda、CorbaScript、ObjVLisp、Object-Z、MODEL-K、XOTcl和MELDC。其中几种语言可追溯日期至1990年代早期并具有学术价值。

Logtalk是Prolog的面向对象扩展，它也支持元类。

资源描述框架（RDF）和统一建模语言（UML）二者都支持元类。