元類

在面向物件程式設計中，元類是一種例項是類的類。普通的類定義的是特定物件的行為，元類定義的則是特定的類及其例項的行為。不是所有面向物件程式語言都支援元類。在它能做的事情之中，元類可以覆寫任何給定方面類行為的程度是不同的。元類可以透過使類成為頭等物件來實現，在這種情況下元類簡單的就是構造類的一個物件。每個語言都有它自己的元物件協議，給出物件、類和元類如何互動的規則。Smalltalk-80元類.在Smalltalk中，所有東西都是物件。此外，Smalltalk是基於類的系統，這意味著所有物件都有一個類，它定義這個物件的結構，和這個物件所理解的訊息。二者在一起蘊含了，在Smalltalk中，類是一個物件，因此類也需要是它的元類的例項。元類在Smalltalk-80系統中的主要角色，是提供協議來初始化類變數，和建立元類的唯一例項的初始化例項。例項聯絡.為了允許類擁有它們自己的方法，和叫作類例項變數它們自己的，Smalltalk-80為每個類codice_1介入了它們自己的元類codice_2。就像例項方法實際上屬於類一樣，類方法實際上屬於元類。在類中定義和，而在元類中定義類例項變數。每個元類在效果上都是單例類。就像連體雙胞胎，類和元類是共生的。元類有一個例項變數codice_3，它指向它結合的類。平常的Smalltalk，不將元類展示為單獨的類，轉而允許一起同時編輯類和它的元類。要得到一個例項的類，需要向它傳送訊息呼叫codice_4方法。類和元類繼承了其超類的codice_5方法，它返回接收者名字的字串。例如，轎車物件codice_6是類codice_7的例項，則codice_8返回codice_7類物件，而codice_10返回codice_11；依次類推，codice_12返回codice_7的元類物件，而codice_14返回依賴於實現，有的是codice_15，即沒有名字，有的是codice_16，即用空格分隔的類名字和codice_17。在早期的Smalltalk-76中，建立新類的方式是向codice_18類傳送codice_19訊息。在Smalltalk-80中，codice_18是元類的基礎類，它是類而不是元類。所有元類都是一個codice_21類的例項。codice_21類是codice_23的例項，而codice_23作為元類，也是codice_21類的例項。繼承聯絡.在Smalltalk-80中，終端物件是一個整數、一個元件、或一臺車等，而類是像codice_26、或codice_27或codice_7等這樣的東西，除了codice_29之外，所有的類都有一個超類。元類所繼承的元類，就是元類對應的類所繼承的類的元類。在一個訊息被髮送到物件的時候，方法的查詢開始於它的類。如果沒有找到則在上行超類鏈，停止於codice_29而不管找到與否。在一個訊息被髮送到一個類的時候，類方法查詢開始於它的元類，並上行超類鏈至codice_31。直至codice_31，元類的超類層級並行於類的超類層級。在Smalltalk-80中，codice_31是codice_18的子類：Object class superclass == Class.類方法的查詢在元類鏈之後仍可繼續下去，所有元類都是codice_18的在繼承層級中的子類，它是所有元類的抽象超類，它描述這些類的一般性質，繼而最終可上溯至codice_29。繼承層級.四個類提供描述新類的設施，下面是它們的繼承層級，和它們提供的主要設施：方法查詢次序.下面是方法查詢次序的辨析：示意圖.下面是兩個示意圖，二者都是縱向連線表示例項聯絡，而橫向連線表示繼承聯絡。例項聯絡以codice_21，將codice_21及其元類放置在最上方的獨立兩行，使得例項聯絡儘量成為樹狀向上匯聚；後者圖示將codice_21及其元類放置在最左邊，使得繼承聯絡儘量都在同一行之上。例子.下列例子展示，從Smalltalk-80派生的Squeak和Pharo的樣例程式碼的結構，它們的繼承層級的根類實際上是codice_92，codice_92封裝了所有物件都必須擁有的極小化的訊息集合，它被設計為引發儘可能多的錯誤，用來支援代理定義。例如Smalltalk-80的codice_29中，錯誤處理訊息codice_95，和系統原始訊息codice_96，就轉而在codice_92中定義了。在示意圖中，縱向的綠色連線，展示繼承聯絡的“子→父”關係，橫向的藍色連線展示例項聯絡的“成員→容器”關係，從codice_98出的發藍色連線，指向codice_98的最小實際容器，它是在呼叫在codice_98上的方法時查詢方法的繼承鏈起點：這個結構由兩個部份構成，使用者部份有四個顯式的物件和類及其兩個隱式的元類：終端物件codice_101和codice_102，它們連線到的類codice_103和codice_104，它們兩個連線到的右側灰色節點表示的隱式的元類，其他的物件都是內建部份。Objective-C元類.在Objective-C中的元類，幾乎同於Smalltalk-80的元類，這是因為Objective-C從Smalltalk引進了很多東西。就像Smalltalk，在Objective-C中例項變數和方法是物件的類定義的。類也是物件，因此它是元類的一個例項。就像Smalltalk，在Objective-C中類方法，簡單的是在類物件上呼叫的方法，因此一個類的類方法，必須定義為在它的元類中的例項方法。因為不同的類有不同的類方法集合，每個類都必須有它自己單獨的元類。類和元類總是成對建立：執行時系統擁有函式codice_105和codice_106來分別的建立和註冊類-元類對。元類沒有名字，但是到任何類物件的指標，可以透過泛化型別codice_18來提及。元類都是相同的類即根類元類的例項，而根類元類是自身的例項。因為類方法是透過繼承聯絡來繼承的，就像Smalltalk，除了根類元類之外，元類繼承聯絡必須並行於類繼承聯絡。不同於Smalltalk，根類元類繼承自根類自身。這確保了所有的元類最終都是根類的子類，從而人們可以將根類的例項方法，它們通常是針對物件有用的實用方法，使用於類物件自身上。Python元類.在Python中，內建的類codice_110是元類。類的定義，不包括它的例項物件的細節，如它們的位元組為單位的大小，它們在記憶體中的二進位制格局，它們是如何分配的，每次建立例項時自動呼叫的codice_111方法，諸如此類。不只是在建立新例項物件的時候，而且在每次訪問例項物件的任何特性的時候，這些細節都起到作用。在沒有元類的語言中，這些細節是在語言規定中定義的，並且不能被覆寫。在Python中，元類codice_110控制著類行為的這些細節，預設定義出的類自身都是codice_110的例項。新的元類可以很容易定義為codice_110的子類從而覆寫它，透過向類定義提供“關鍵字引數”codice_115就可以使用這個新的元類。»&gt; type(b)»&gt; print(type(B), B.__bases__, [*B.__dict__])»&gt; print(type(A), A.__bases__, [*A.__dict__])»&gt; print(type(M), M.__bases__, [*M.__dict__])»&gt; print(type(c), c.__bases__)»&gt; print(type(r), r.__bases__)»&gt; sorted({*r.__dict__} &amp; {*c.__dict__})['__delattr__', '__dir__', '__doc__', '__getattribute__', '__init__', '__new__', '__repr__', '__setattr__', '__sizeof__']»&gt; sorted({*r.__dict__} - {*c.__dict__})['__class__', '__eq__', '__format__', '__ge__', '__gt__', '__hash__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__reduce__', '__reduce_ex__', '__str__', '__subclasshook__']»&gt; sorted({*c.__dict__} - {*r.__dict__})['__abstractmethods__', '__base__', '__bases__', '__basicsize__', '__call__', '__dict__', '__dictoffset__', '__flags__', '__instancecheck__', '__itemsize__', '__module__', '__mro__', '__name__', '__prepare__', '__qualname__', '__subclasscheck__', '__subclasses__', '__text_signature__', '__weakrefoffset__', 'mro']例子.考慮下面這個最簡單的Python類：class Car:def __init__(self, *args, **kwargs):self.__dict__.update(kwargs)def __call__(self, **kwargs):self.__dict__.update(kwargs)@propertydef description(self):"""返回這輛車的描述."""return " ".join(str(value) for value in self.__dict__.values())»&gt; new_car = Car(make='Toyota', model='Prius', year=2005, engine='Hybrid')»&gt; new_car(color='Green')»&gt; new_car.description'Toyota Prius 2005 Hybrid Green'上面的例子包含了一些程式碼來處理初始化特性，也可以使用元類來完成這種任務：class AttributeInitType(type):def __new__(*args, **kwargs):"""返回建立的例項類."""cls = type.__new__(*args, **kwargs)def call(self, **kwargs):self.__dict__.update(kwargs)cls.__call__ = call # 為例項類增加__call__方法return clsdef __call__(cls, *args, **kwargs):"""返回為例項類建立的例項物件."""obj = type.__call__(cls, *args) # 以正常預設方式建立例項物件。obj.__dict__.update(**kwargs) # 在這個新物件上設定屬性。return obj這個元類只覆寫例項類和物件建立部份功能。元類行為的所有其他方面仍由codice_110處理。現在可以重寫類codice_7使用這個新元類：class Car(object, metaclass=AttributeInitType):def __init__(self, *args): pass # 接收未預期的位置實際引數@propertydef description(self):"""返回這輛車的描述."""return " ".join(str(value) for value in self.__dict__.values())Ruby元類.Ruby透過介入其自稱的特徵類，提煉了Smalltalk-80的元類概念，去除了codice_21類，並重新定義了codice_119對映。變更可以圖示如下：特別要注意在Smalltalk的隱含的元類和Ruby類的特徵類之間的對應。Ruby的特徵類模型，使得隱式元類概念完全統一：所有物件codice_98，都有它自己的元物件，它叫作codice_98的特徵類，它比codice_98高一個元層級。高階特徵類通常是純粹概念上的存在，在大多數Ruby程式中，它們不包含任何方法也不儲存任何資料。下面的示意圖展示Ruby樣例程式碼的核心結構。這裡的灰色節點表示開啟codice_103和codice_102特徵類後擴張出來的特徵類。圖示還展示了Ruby中特徵類的惰性求值。codice_102物件可以有它的特徵類，作為向codice_102增加“單例方法”的結果而被求值。在語言和工具中的支援.下面是支援元類的一些最顯著的程式語言。一些不甚廣泛傳播的語言支援元類，包括、OpenC++、OpenAda、、ObjVLisp、、MODEL-K、和MELDC。其中幾種語言可追溯日期至1990年代早期並具有學術價值。是Prolog的面向物件擴充套件，它也支援元類。資源描述框架二者都支援元類。