元類

在面向對象程序設計中，元類（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`上的方法時查找方法的繼承鏈起點：

```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`及其元類放置在最左邊，使得繼承聯繫儘量都在同一行之上。

- 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實例）。
- 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中實例變量和方法是對象的類定義的。類也是對象，因此它是元類的一個實例。

- 在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`來使用這種自定義元類。例如：

```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`本徵類後擴張出來的本徵類。

```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）二者都支持元類。