メタクラス

オブジェクト指向プログラミングにおいて、メタクラスとは、その構文定義がクラス自身の構文定義に使用されるクラスのことである。オブジェクトの動作を定義する通常のクラスとは異なり、メタクラスはクラスとそのインスタンスの動作を指定する。すべてのオブジェクト指向プログラミング言語がメタクラスの概念をサポートしているわけではない。サポートしている言語でも、メタクラスがクラスの動作に対して持つ制御の範囲は様々である。メタクラスは多くの場合、クラスを第一級オブジェクトとして扱い、メタクラスをこれらのクラスを作成および管理するオブジェクトにすることで実装される。各プログラミング言語は独自のメタオブジェクトプロトコルに準拠しており、これはオブジェクト、クラス、およびメタクラス間の相互作用を決定する規則である。[ 1 ]メタクラスは、コード生成を自動化し、フレームワーク開発を強化するために利用される。[ 2 ]

Pythonの例

Pythonでは、組み込みクラスはtypeメタクラスです。[ 3 ] [ 4 ] [ 5 ]次の単純なPythonクラスを考えてみましょう

クラス:メーカー: strモデル: str年式: int: strdef __init__ ( self , make : str , model : str , year : int , color : str ) - > None : 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、オブジェクトのバイトサイズCar、メモリ内でのバイナリレイアウト、オブジェクトの割り当て__init__方法、 が作成されるたびに メソッドが自動的に呼び出されるかどうCarかなどの詳細は含まれていません。 これらの詳細は、新しいCarオブジェクトが作成されるときだけでなく、 の任意の属性がアクセスされるたびにも関係しますCar。 メタクラスのない言語では、これらの詳細は言語仕様によって定義され、オーバーライドできません。 Python では、メタクラス - がtypeのこれらの動作の詳細を制御しますCar。 の代わりに別のメタクラスを使用することで、これらをオーバーライドできますtype

make上記の例には、4つの属性、modelyear、に関する冗長なコードが含まれていますcolor。カスタムメタクラスを使用することで、この冗長性の一部を削減できます。Pythonでは、メタクラスは のサブクラスとして定義するのが最も簡単ですtype

入力からimport Anyclass AttributeInitType ( type ): def __call__ ( self , * args : tuple [ Any ], ** kwargs : dict [ str , Any ]) -> object : """ 新しいインスタンスを作成します。 """# まず、通常のデフォルトの方法でオブジェクトを作成します。obj : object = 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-80 メタクラス階層を UML 図で表す
Smalltalk におけるクラスとメタクラス間の継承とインスタンス関係の図

Smalltalkでは、すべてがオブジェクトです。さらに、Smalltalkはクラスベースのシステムです。つまり、すべてのオブジェクトには、そのオブジェクトの構造(つまり、オブジェクトが持つインスタンス変数)と、オブジェクトが理解するメッセージを定義するクラスがあります。これらを合わせると、Smalltalkにおけるクラスはオブジェクトであり、したがってクラスはクラスのインスタンス(メタクラスと呼ばれる)である必要があります。

例えば、 car オブジェクトcはクラス のインスタンスですCar。クラス もCarまたオブジェクトであり、したがって とCar呼ばれるメタクラスのインスタンスCar classです。メタクラス名に空白があることに注意してください。メタクラス名は Smalltalk 式であり、評価されるとメタクラスオブジェクトが生成されます。したがって、 を評価すると、の名前を持つCar classのメタクラスオブジェクトが生成されます(のメタクラス名を返す を評価することで、これを確認できます)。 CarCar classCar class nameCar

クラスメソッドは実際にはメタクラスに属し、インスタンスメソッドは実際にはクラスに属します。オブジェクト にメッセージが送信されると2、メソッドの検索が 内で開始されますInteger。メソッドが見つからない場合、スーパークラスチェーンを遡り、見つかったかどうかにかかわらず Object で停止します。

メッセージが に送信されると、Integerメソッドの検索は から開始されInteger class、スーパークラスチェーンを まで遡って進みます。ここまでのところ、メタクラスの継承チェーンはクラスの継承チェーンと全く同じであることに注意してください。しかし、は のサブクラスであるObject classため、メタクラスのチェーンはさらに拡張されます。すべてのメタクラスは Class のサブクラスです。 Object classClass

初期のSmalltalkでは、 というメタクラスが1つしかありませんでしたClass。これは、すべてのクラスが持つメソッド、特に新しいオブジェクトを作成するメソッド(つまり )が同じであることを意味していましたnew。クラスが独自のメソッドとインスタンス変数(クラスインスタンス変数と呼ばれ、クラス変数と混同しないでください)を持つことができるように、Smalltalk-80では各クラスにC独自のメタクラスが導入されましたC class。これは、各メタクラスが実質的にシングルトンクラスであることを意味します。

メタクラスは互いに異なる動作をする必要がないため、すべてのメタクラスは という1つのクラスのインスタンスとなりますMetaclass。 のメタクラスはMetaclassと呼ばれ、Metaclass classこれもまたクラス のインスタンスですMetaclass

Smalltalk-80では、すべてのクラス( を除くObject)にはスーパークラスが存在します。すべてのメタクラスの抽象スーパークラスClassは であり、これはクラスの一般的な性質を記述します。

メタクラスのスーパークラス階層は、クラス を除いて、クラスのスーパークラス階層と類似していますObject。すべてのメタクラスは のサブクラスであるClassため、次のようになります。

  • Object class superclass == Class.

結合双生児のように、クラスとメタクラスは同時に生まれます。Metaclassはインスタンス変数を持ちthisClass、これは結合されたクラスを指します。通常のSmalltalkクラスブラウザでは、メタクラスは個別のクラスとして表示されないことに注意してください。クラスブラウザでは、クラスとそのメタクラスを同時に編集できます。

メタクラス階層内のクラス名は、同じ名前の概念と混同されやすいです。例えば:

  • Objectは、すべてのオブジェクトに共通するメソッドを提供する基本クラスです。「オブジェクト」とは、整数、ウィジェットCar、などです。
  • Classは、すべてのクラスに共通するメソッドを提供するメタクラスのベースです(ただし、それ自体はメタクラスではありません)。「クラス」はInteger、、、、WidgetなどのようなものですCar
  • Metaclassすべてのメタクラスに共通のメソッドを提供します。

4つのクラスは、新しいクラスを記述するための機能を提供します。これらのクラスはObjectからの継承階層を持ち、主な機能は以下のとおりです。

オブジェクト - クラスアクセスなど、すべてのオブジェクトに共通するデフォルトの動作
動作 -メソッドのコンパイルとオブジェクトの作成/実行の 最小状態
ClassDescription (抽象クラス) - クラス/変数の命名、コメント
クラス - スーパークラスと同様の、より包括的な機能
メタクラス - クラス変数の初期化、インスタンス作成メッセージ

Rubyでは

Rubyは、固有クラスの導入、クラスの削除、クラスマップの再定義(または再非定義)によって、Smalltalk-80のメタクラスの概念を洗練させましたMetaclass。この変更は次のように図式化できます。[ 6 ]

Smalltalk-80
クラス
暗黙的なメタクラス
  
終端オブジェクト
Ruby
クラス
クラスの固有クラス
固有クラスの固有クラス
終端オブジェクト
終端オブジェクトの固有クラス

特に、Smalltalkの暗黙的メタクラスとRubyのクラスの固有クラスとの対応に注目してください。Rubyの固有クラスモデルは、暗黙的メタクラスの概念を完全に統一しています。つまり、すべてのオブジェクトxは、 x固有クラスと呼ばれる独自のメタオブジェクトを持ち、これはxより1つメタレベル高いです。「高階」固有クラスは通常、純粋に概念的に存在し、ほとんどのRubyプログラムでは、メソッドを含んだり、(その他の)データを保存したりしません。[ 7 ]

以下の図は、Smalltalk-80 と Ruby のサンプルのコア構造を比較したものです。[ 8 ] どちらの言語でも、構造は循環オブジェクト (つまり、青または緑のリンクの組み合わせによって形成される循環内に現れるオブジェクト) を含む組み込み部分と、クラスAB 、終端オブジェクトuとの 4 つの明示的なオブジェクトを持つユーザー部分で構成されていますv。緑のリンクは子→親の継承関係 (暗黙の上方向) を示し、青のリンクは補完的なメンバー→コンテナーのインスタンス化関係 ( xからの青のリンクは、 x でメソッドが呼び出されたときのメソッド検索の開始点である、xの最小の実際のコンテナーを指します)を示します。灰色のノードは固有クラス (Smalltalk-80 の場合は暗黙のメタクラス) を表示します。

Smalltalk-80 Ruby
Smalltalk-80における暗黙のメタクラス - サンプル構造Ruby の固有クラス - サンプル構造

右の図は、Rubyにおける固有クラスの遅延評価の様子を示しています。 オブジェクトに特異メソッドvを追加することで、その固有クラスを評価(割り当て)することができます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におけるクラスとメタクラス間の継承とインスタンス関係を示す図。Objective-Cには複数のルートクラスがあり、各ルートクラスはそれぞれ独立した階層構造を持つことに注意してください。この図は、例としてNSObjectルートクラスの階層構造のみを示しています。他のルートクラスも同様の階層構造を持ちます。

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 ]

C++提案

ISO C++委員会のハーブ・サッターは、 C++17の機能を使用してC++20にメタクラスを組み込むことを初めて提案しました。[ 11 ] C++26リフレクションが採用されたため、この論文は改訂されました。[ 12 ]

classその主な目的は、C++の抽象化を定義済み語彙( 、structunion、など)を超えて拡張し、採用可能な語彙( 、enumなど)を可能にすることです。これにより、ルールの暗記に縛られない、より自由なプログラミングアプローチが可能になります。この機能により、非標準機能(インターフェースなど)をライブラリ内のコア機能として表現できるようになり、標準化された機能を用いて特定の情報を表現するために専用のコンパイラ( Qt mocC++/CXなど)を使用する必要がなくなります。 interfacevalue

コードインジェクション(C++リフレクションに提案されたもう1つの機能で、ソースコードにトークンシーケンスのインジェクションを追加するもの)[ 13 ]を使用すると、関数interfaceを使用してJava / C#スタイルの「」を作成できますconsteval

stdをインポートしますstd :: stringを使用します。std :: string_viewを使用します。std :: meta :: info使用します[[ nodiscard ]] consteval info makeInterfaceFunctions ( info proto ) noexcept { info ret = ^^ {}; for ( info mem : std :: meta :: members_of ( proto )) { if ( std :: meta :: is_nonspecial_member_function ( mem )) { ret = ^^ { \ tokens ( ret ) virtual [ : \ ( std :: meta :: return_type_of ( mem )) : ] \ id ( std :: meta :: identifier_of ( mem )) ( \ tokens ( std :: meta :: parameter_list_of ( mem ))) = 0 ; }; } else if ( std :: meta :: is_variable ( mem )) { // コンパイル時エラーを報告します (C++ ではまだ機能ではありません) } } return ret ; }consteval void interface ( info proto ) noexcept { string_view name = std :: meta :: identifier_of ( proto ); std :: meta :: queue_injection ( ^^ { class \ id ( name ) { public : // インターフェース関数を作成する\ tokens ( makeInterfaceFunctions ( proto )) // デストラクタvirtual ~ \ id ( name )() = default ; // コンストラクタ\ id ( name )() = default ; // コピー コンストラクタを削除する\ id ( name )( \ id ( name ) const & ) = delete ; // コピー代入演算子を削除するvoid operator = ( \ id ( name ) const & ) = delete ; }; }); }// "interface" を使用します: class ( interface ) IFoo { int f (); void g ( string s ); };

複数のメタクラス型を適用することも可能です。例えば、メタクラス と の場合xyと記述できます。 class(x,y)MyClass{/* ... */};

本論文では、この抽象化によって、自動生成関数やカスタマイズ可能なデフォルト、セマンティクス、制約を生成することで、定型的な記述を大幅に削減できると提案しています。さらに、メタクラス機能を用いてそのような機能を表現できるようにすることで、新たな言語機能の提案を作成する必要性も排除されます。

言語とツールのサポート

以下は、メタクラスをサポートする最も有名なプログラミング言語の一部です。

メタクラスをサポートするあまり普及していない言語としては、OpenJava [ 14 ]OpenC++ [ 15 ] [ 16 ]OpenAdaCorbaScriptObjVLispObject-ZMODEL-KXOTclMELDCなどがあります。これらの言語のいくつかは1990年代初頭に登場し、学術的にも興味深いものです。[ 17 ]

イントロスペクションjava.lang.Class<T>のためのJava機能。メタクラスに似ていますが、実際にはメタクラスではありません。

Prologのオブジェクト指向拡張であるLogtalkもメタクラスをサポートしています。

リソース記述フレームワーク(RDF) と統一モデリング言語(UML) はどちらもメタクラスをサポートしています。

メタクラスはC++の将来のバージョンに組み込むことが提案されていましたが、現在は組み込まれていない機能です。[ 11 ]

参照

参考文献

  1. ^ Forman, Ira R.; Danforth, Scott (1999). Putting Metaclasses to Work . Addison-Wesley. ISBN 0-201-43305-2
  2. ^ AloorRavi, Sulekha (2022). Pythonによるメタプログラミング. バーミンガム: Packt Publishing
  3. ^ IBM Metaclass programming in Python, parts 1 Archived 3 September 2008 at the Wayback Machine 2 Archived 23 August 2008 at the Wayback Machine 3 Archived 21 March 2009 at the Wayback Machine
  4. ^ Artima Forum: Python 3.0 のメタクラス(パート 1/2) 2020 年 9 月 24 日にWayback Machineにアーカイブ(パート 2/2) 2016 年 4 月 9 日にWayback Machineアーカイブ
  5. ^ Mertz, David. 「Pythonメタクラスプログラミング入門」 ONLamp . 2003年4月30日時点のオリジナルよりアーカイブ。 2006年6月28日閲覧
  6. ^ 「Rubyオブジェクトモデル:Smalltalk-80との比較」2020年1月17日時点のオリジナルよりアーカイブ2012年2月10日閲覧。
  7. ^ Perrotta, Paolo (2010). Metaprogramming Ruby . Pragmatic Bookshelf. ISBN 978-1-934356-47-0. 2016年6月9日時点のオリジナルよりアーカイブ。2013年11月21日閲覧。
  8. ^ 「オブジェクトメンバーシップ:オブジェクトテクノロジーの中核構造」2021年5月6日時点のオリジナルよりアーカイブ2012年8月24日閲覧。
  9. ^ “Struct” . 2025年5月3日時点のオリジナルよりアーカイブ2025年4月28日閲覧。
  10. ^ 「Objective-Cのメタクラスとは何か?」 Cocoa with Love . 2010年1月17日. 2011年8月6日時点のオリジナルよりアーカイブ。 2011年7月22日閲覧
  11. ^ a b Sutter, Herb (2017年6月18日). 「P0707 R0 - メタクラス」(PDF) . open-std.org . WG21. 2020年11月11日時点のオリジナルよりアーカイブ(PDF) . 2018年8月8日閲覧
  12. ^ Sutter, Herb (2024年10月12日). 「Consteval関数 + リフレクション + 生成を用いた宣言型クラスオーサリング(別名:生成型C++のためのメタクラス)」(PDF) . open-std.org . WG21 . 2025年12月14日閲覧
  13. ^ Andrei Alexandrescu、Barry Rezvin、Daveed Vandevoorde (2024年7月16日). 「トークンシーケンスによるコードインジェクション」 . open-std.org . WG21.{{cite web}}:CS1メンテナンス:複数の名前:著者リスト(リンク
  14. ^辰堀道明 (2007年10月14日)。「OJ: 拡張可能な Java」csg.ci.iu-tokyo.ac.jp。東京大学。2014年8月13日のオリジナルからアーカイブ。
  15. ^千葉茂 (2007年10月14日). 「OpenC++ホームページ」 . csg.ci.iu-tokyo.ac.jp . 東京大学. 2014年8月21日時点のオリジナルよりアーカイブ。
  16. ^千葉茂. 「OpenC++へようこそ」 . opencxx.sourceforge.net . 東京大学. 2025年12月10日閲覧
  17. ^ 「メタクラスを用いたJavaでのミックスインの実装」(PDF)2007年10月16日時点のオリジナル(PDF)からアーカイブ。 2007年11月27日閲覧