オブジェクト指向プログラミングにおいて、メタクラスとは、インスタンス自体がクラスであるクラスである。オブジェクトの動作を定義する通常のクラスとは異なり、メタクラスはクラスとそのインスタンスの動作を指定する。すべてのオブジェクト指向プログラミング言語がメタクラスの概念をサポートしているわけではない。サポートしている言語でも、メタクラスがクラスの動作に対して持つ制御の範囲はさまざまである。メタクラスは、クラスを第一級市民として扱うことによって実装されることが多く、メタクラスはこれらのクラスを作成および管理するオブジェクトとなる。各プログラミング言語は、オブジェクト、クラス、およびメタクラス間の相互作用を決定する規則である独自のメタオブジェクト プロトコルに準拠している。 [1]メタクラスは、コード生成を自動化し、フレームワーク開発を強化するために利用される。[2]
Pythonの例
Pythonでは、組み込みクラスはtypeメタクラスです。[3] [4] [5]この単純なPythonクラスを考えてみましょう。
クラス Car :
def __init__ ( self 、 make : str 、 model : str 、 year : int 、 color : str ) :
self.make = make self.model = model self.year = year self.color = color
@property
def description ( self ) -> str :
"""この車の説明を返します。""" return f " { self . color } { self . make } { self . model } "
実行時には、Car自体は のインスタンスですtype。上記のクラスのソース コードには、オブジェクトCarのバイト サイズ、メモリ内のバイナリ レイアウト、割り当て方法、 が作成されるたびに メソッドが自動的に呼び出されるかどうかなどの詳細は含まれていません。これらの詳細は、新しいオブジェクトが作成されるときだけでなく、 の任意の属性がアクセスされるたびにも関係します。メタクラスのない言語では、これらの詳細は言語仕様によって定義され、オーバーライドできません。Python では、メタクラス - - が の動作のこれらの詳細を制御します。 の代わりに別のメタクラスを使用することで、これらをオーバーライドできます。
Car__init__CarCarCartypeCartype
上記の例には、 、make、の4 つの属性に関連する冗長なコードがいくつか含まれています。カスタム メタクラスを使用すると、この冗長性の一部を排除することができます。Python では、メタクラスは のサブクラスとして定義するのが最も簡単です。
modelyearcolortype
class AttributeInitType ( type ):
def __call__ ( self , * args , ** kwargs ):
"""新しいインスタンスを作成します。"""
# まず、通常のデフォルトの方法でオブジェクトを作成します。obj = type . __call__ ( self , * args )
# さらに、新しいオブジェクトの属性を設定します。
for name , value in kwargs . items ():
setattr ( obj , name , value )
# 新しいオブジェクトを返します。
return obj
このメタクラスはオブジェクトの作成のみをオーバーライドします。クラスとオブジェクトの動作のその他のすべての側面は、引き続き によって処理されますtype。
これで、このメタクラスを使用するようにクラスを書き直すことができます。Python 3 では、クラス定義に
Car「キーワード引数」を提供することでこれを実現します。metaclass
class Car ( object , metaclass = AttributeInitType ):
@property
def description ( self ) -> str :
"""この車の説明を返します。""" return " " . join ( str ( value ) for value in self . __dict__ . values ())
結果のオブジェクトはCar通常どおりインスタンス化できますが、任意の数のキーワード引数を含めることができます。
new_car = 車(メーカー= 'トヨタ' 、 モデル= 'プリウス' 、 年式= 2005 、 色= 'グリーン' 、 エンジン= 'ハイブリッド' )
Smalltalk-80では


Smalltalkでは、すべてがオブジェクトです。さらに、Smalltalkはクラス ベースのシステムです。つまり、すべてのオブジェクトには、そのオブジェクトの構造 (つまり、オブジェクトが持つインスタンス変数) とオブジェクトが理解するメッセージを定義するクラスがあります。これらを合わせると、Smalltalk のクラスはオブジェクトであり、したがってクラスはクラスのインスタンス (メタクラスと呼ばれる) である必要があります。
たとえば、 car オブジェクトcはクラス のインスタンスですCar。 クラス は再びオブジェクトであり、 と呼ばれるCarのメタクラスのインスタンスです。 メタクラスの名前に空白があることに注意してください。 メタクラスの名前は Smalltalk 式であり、評価されるとメタクラス オブジェクトが生成されます。 したがって、 を評価すると、の名前を持つのメタクラス オブジェクトが生成されます(のメタクラスの名前を返す を評価することでこれを確認できます)。
CarCar classCar classCarCar classCar class nameCar
クラス メソッドは実際にはメタクラスに属し、インスタンス メソッドは実際にはクラスに属します。メッセージがオブジェクト に送信されると2、 でメソッドの検索が開始されますInteger。見つからない場合は、スーパークラス チェーンを上って進み、見つかったかどうかに関係なく Object で停止します。
メッセージが に送信されると、Integerメソッドの検索が から開始されInteger class、スーパークラス チェーンを まで進みます。これまでのところ、メタクラス継承チェーンはクラス継承チェーンの直後に続いていることに注意してください。ただし、は のサブクラスであるObject classため、メタクラス チェーンはさらに拡張されます。すべてのメタクラスは Class のサブクラスです。
Object classClass
初期の Smalltalk では、 というメタクラスが 1 つしかありませんでしたClass。これは、すべてのクラスが持つメソッド、特に新しいオブジェクトを作成するメソッド (つまり ) が同じであることを意味していました。クラスが独自のメソッドと独自のインスタンス変数 (クラス インスタンス変数と呼ばれ、クラス変数newと混同しないでください) を持つことができるように、Smalltalk-80 では各クラスに独自のメタクラスが導入されました。つまり、各メタクラスは実質的にシングルトンクラスです。
CC class
メタクラスが互いに異なる動作をする必要はないため、すべてのメタクラスは という 1 つのクラスのインスタンスになりますMetaclass。 のメタクラスはMetaclassと呼ばれMetaclass class、これもクラス のインスタンスですMetaclass。
Smalltalk-80 では、すべてのクラス ( を除くObject) にスーパークラスがあります。すべてのメタクラスの抽象スーパークラスClassは であり、クラスの一般的な性質を記述します。
メタクラスのスーパークラス階層は、クラス を除いて、クラスのスーパークラス階層と類似していますObject。すべてのメタクラスは のサブクラスであるClassため、次のようになります。
Object class superclass == Class.
結合双生児のように、クラスとメタクラスは一緒に生まれます。には、結合クラスを指すMetaclassインスタンス変数があります。通常の Smalltalkクラス ブラウザーでは、メタクラスが個別のクラスとして表示されないことに注意してください。代わりに、クラス ブラウザーでは、クラスとそのメタクラスを同時に編集できます。
thisClass
メタクラス階層内のクラス名は、同じ名前の概念と混同されやすいです。例:
Objectは、すべてのオブジェクトに共通のメソッドを提供する基本クラスです。「オブジェクト」は整数、ウィジェット、Carなどです。Classは、すべてのクラスに共通するメソッドを提供するメタクラスのベースです (ただし、それ自体はメタクラスではありません)。「クラス」は、、、、などのようなものIntegerです。WidgetCarMetaclassすべてのメタクラスに共通のメソッドを提供します。
4 つのクラスは、新しいクラスを記述する機能を提供します。それらの継承階層(Object から) と、それらが提供する主な機能は次のとおりです。
- オブジェクト - クラスアクセスなど、すべてのオブジェクトに共通するデフォルトの動作
- 動作 - メソッドのコンパイルとオブジェクトの作成/実行
の最小状態
- ClassDescription (抽象クラス) - クラス/変数の命名、コメント
- クラス - スーパークラスと同様の、より包括的な機能
- メタクラス - クラス変数の初期化、インスタンス作成メッセージ
- ClassDescription (抽象クラス) - クラス/変数の命名、コメント
- 動作 - メソッドのコンパイルとオブジェクトの作成/実行
の最小状態
ルビーで
MetaclassRubyは、固有クラスの導入、クラスの削除、クラスマップの再定義(または再定義)によって、Smalltalk-80のメタクラスの概念を洗練させました。この変更は次のように図式化できます。 [6]
特に Smalltalk の暗黙的メタクラスと Ruby のクラスの固有クラスとの対応に注目してください。Ruby の固有クラス モデルは暗黙的メタクラスの概念を完全に統一します。つまり、すべてのオブジェクトxには、 xの固有クラスと呼ばれる独自のメタオブジェクトがあり、これはxより 1 メタレベル高いです。「高次の」固有クラスは通常、純粋に概念的に存在します。ほとんどの Ruby プログラムでは、メソッドを含まず、(他の) データも格納しません。[7]
以下の図は、Smalltalk-80 と Ruby のサンプルのコア構造を比較したものです。[8]
どちらの言語でも、構造は、循環オブジェクト (つまり、青または緑のリンクの組み合わせによって形成されるサイクル内に現れるオブジェクト) を含む組み込み部分と、クラスAとB
、終端オブジェクトuとという4 つの明示的なオブジェクトを持つユーザー部分でv構成されています。緑のリンクは、継承の子→親関係 (暗黙の上方向) を示し、青のリンクはインスタンス化の補完的なメンバー→コンテナー関係 ( xからの青のリンクは、 xでメソッドが呼び出されたときのメソッド検索の開始点である、xの最小の実際のコンテナーを指します) を示します。灰色のノードは、固有クラス (Smalltalk-80 の場合は暗黙のメタクラス) を表示します。
右の図は、 Ruby における eigenclasses の遅延評価の図も示しています。にシングルトン メソッドvを追加することで、オブジェクトの eigenclasses を評価 (割り当て) できるようになります。
v
Ruby の というイントロスペクション方法によればclass、すべてのクラス (およびすべての固有クラス) のクラスは常にClassクラスです (c図では で示されています)。
Class、および は、Structクラスをインスタンスとして持つ唯一のクラスです。[9] [異議あり–議論]のサブクラス化はClass許可されていません。 メタクラスの標準定義に従うと、およびClassがStructRuby の唯一のメタクラスであると結論付けることができます。 これは、Smalltalk-80 ではすべてのクラスが独自のメタクラスを持つため、Ruby と Smalltalk の対応関係と矛盾しているように思われます。 この矛盾は、classRuby と Smalltalk のイントロスペクション方法の不一致に基づいています。 マップx ↦ x. はclass終端オブジェクトでは一致しますが、クラスへの制限が異なります。 すでに上で述べたように、クラス の場合x、Ruby 式はx.class常に と評価されますClass。 Smalltalk-80 では、 がxクラスの場合、式はx classRuby のx.singleton_class
– に対応し、これは の固有クラスと評価されますx。
Objective-Cの場合

Objective-C のメタクラスは Smalltalk-80 のメタクラスとほぼ同じです。Objective-C は Smalltalk から多くのものを借用しているので、これは驚くことではありません。Smalltalk と同様に、Objective-C では、インスタンス変数とメソッドはオブジェクトのクラスによって定義されます。クラスはオブジェクトであるため、メタクラスのインスタンスです。
Smalltalk と同様に、Objective-C では、クラス メソッドはクラス オブジェクトで呼び出されるメソッドに過ぎないため、クラスのクラス メソッドはメタクラスのインスタンス メソッドとして定義する必要があります。クラスによってクラス メソッドのセットが異なる場合があるため、各クラスには独自のメタクラスが必要です。クラスとメタクラスは常にペアで作成されます。ランタイムには、クラスとメタクラスのペアをそれぞれ作成および登録する
関数objc_allocateClassPair()とがあります。objc_registerClassPair()
メタクラスには名前がありませんが、任意のクラス オブジェクトへのポインターはジェネリック型 (任意のオブジェクトへのポインターに使用される
Class型と同様)を使用して参照できます。id
クラス メソッドは Smalltalk のように継承を通じて継承されるため、メタクラスはルート クラスを除いて、クラスの継承スキームと並行する継承スキームに従う必要があります (たとえば、クラス A の親クラスがクラス B である場合、A のメタクラスの親クラスは B のメタクラスです)。
Smalltalk とは異なり、ルート クラスのメタクラスはルート クラス自体 (通常はCocoaNSObjectフレームワークを使用) から継承します。これにより、すべてのクラス オブジェクトが最終的にルート クラスのインスタンスになることが保証され、ルート クラスのインスタンス メソッド (通常はオブジェクトにとって便利なユーティリティ メソッド) をクラス オブジェクト自体で使用できるようになります。
メタクラス オブジェクトは異なる動作をしないため (メタクラスにクラス メソッドを追加することはできないため、メタクラス オブジェクトはすべて同じメソッドを持ちます)、それらはすべて同じクラス (ルート クラスのメタクラス) のインスタンスです (Smalltalk とは異なります)。したがって、ルート クラスのメタクラスはそれ自身のインスタンスです。この理由は、すべてのメタクラスがルート クラスから継承されるため、ルート クラスのクラス メソッドを継承する必要があるためです。[10]
言語とツールのサポート
以下は、メタクラスをサポートする最も有名なプログラミング言語の一部です。
- Common Lisp、CLOS経由
- Delphiおよびそれに影響を受けたObject Pascalの他のバージョン
- グルーヴィー
- オブジェクティブC
- パイソン
- Perl(メタクラスプラグマ経由)およびMoose
- ルビー
- 雑談
- C++(将来の標準バージョンに組み込むことが提案されている)[11]
メタクラスをサポートするあまり普及していない言語としては、OpenJava、OpenC++、OpenAda、CorbaScript、ObjVLisp、Object-Z、MODEL-K、XOTcl、MELDCなどがある。これらの言語のいくつかは1990年代初頭に登場し、学術的に興味深いものである。[12]
Prologのオブジェクト指向拡張であるLogtalkもメタクラスをサポートしています。
リソース記述フレームワーク(RDF) と統一モデリング言語(UML) はどちらもメタクラスをサポートしています。
参照
参考文献
- ^ Ira R. Forman および Scott Danforth (1999)。メタクラスの活用。Addison- Wesley。ISBN 0-201-43305-2。
- ^ AloorRavi、Sulekha (2022)。Pythonによるメタプログラミング。バーミンガム:Packt Publishing。
- ^ IBM Metaclass プログラミング in Python、パート 1、2008-09-03 にWayback Machineでアーカイブ、2 および 3
- ^ Artima フォーラム: Python 3.0 のメタクラス (パート 1/2) (パート 2/2)
- ^ David Mertz. 「Python メタクラス プログラミング入門」ONLamp。 2003 年 4 月 30 日時点のオリジナルよりアーカイブ。 2006 年6 月 28 日閲覧。
- ^ 「Ruby オブジェクト モデル: Smalltalk-80 との比較」。
- ^ Paolo Perrotta (2010). Metaprogramming Ruby. Pragmatic Bookshelf. ISBN 978-1-934356-47-0。
- ^ 「オブジェクト メンバーシップ: オブジェクト テクノロジーの中核構造」。
- ^ “Struct”. Ruby Doc . 2015年5月1日閲覧。
- ^ Cocoa with Love: Objective-C のメタクラスとは何ですか?
- ^ Herb Sutter . 「メタクラス」(PDF)。
- ^ 「メタクラスを使用した Java でのミックスインの実装」(PDF)。2007 年 10 月 16 日のオリジナル(PDF)からアーカイブ。2007 年 11 月 27 日に取得。
外部リンク
- メタクラスとは何ですか?
