クラスはメタクラスのインスタンスであるため、メタクラスの「インスタンスメソッド」がクラスメソッドのように動作することは予想外ではありません。
しかし、はい、違いがあります-そしてそれらのいくつかは意味論以上のものです:
- 最も重要な違いは、メタクラスのメソッドがクラスインスタンスから「見えない」ことです。これは、Pythonでの属性ルックアップ(簡略化された方法-記述子が優先される場合があります)がインスタンスの属性を検索するために発生します。インスタンスに属性が存在しない場合、Pythonはそのインスタンスのクラスを検索し、検索が続行されます。クラスのスーパークラスではなく、クラスのクラス。Python stdlibは、
abc.ABCMeta.register
メソッドでこの機能を使用します。クラス自体に関連するメソッドは、競合することなくインスタンス属性として自由に再利用できます(ただし、メソッドは依然として競合します)。
- もう1つの違いは明らかですが、メタクラスで宣言されたメソッドはいくつかのクラスで使用でき、それ以外の場合は関連しません-異なるクラス階層があり、それらが扱うものにはまったく関連がなく、すべてのクラスにいくつかの共通機能が必要な場合、両方の階層のベースとして含める必要があるミックスインクラスを考え出す必要があります(たとえば、すべてのクラスをアプリケーションレジストリに含める場合など)。(注意:ミックスインはメタクラスよりも優れた呼び出しになる場合があります)
- classmethodは特殊な「classmethod」オブジェクトですが、メタクラスのメソッドは通常の関数です。
したがって、クラスメソッドが使用するメカニズムは「記述子プロトコル」であることが起こります。通常の関数は、インスタンスから取得したときに引数__get__
を挿入self
し、クラスから取得したときにその引数を空のままにするメソッドを備えてい classmethod
ますが、オブジェクトには__get__
、クラス自体(「所有者」)を両方の状況での最初のパラメーター。
これはほとんどの場合実際的な違いはありませんが、メタクラスのメソッドがmeta.method
関数を取得するために、デコレータを動的に追加するために、関数としてメソッドにアクセスする場合は、関数にアクセスする必要がある場合は、すぐに使用できます、cls.my_classmethod.__func__
それを使用してクラスメソッドからclassmethod
オブジェクトを取得する必要があります(ラッピングを行う場合は、別のオブジェクトを作成して割り当て直す必要があります)。
基本的に、これらは2つの例です。
class M1(type):
def clsmethod1(cls):
pass
class CLS1(metaclass=M1):
pass
def runtime_wrap(cls, method_name, wrapper):
mcls = type(cls)
setattr(mcls, method_name, wrapper(getatttr(mcls, method_name)))
def wrapper(classmethod):
def new_method(cls):
print("wrapper called")
return classmethod(cls)
return new_method
runtime_wrap(cls1, "clsmethod1", wrapper)
class CLS2:
@classmethod
def classmethod2(cls):
pass
def runtime_wrap2(cls, method_name, wrapper):
setattr(cls, method_name, classmethod(
wrapper(getatttr(cls, method_name).__func__)
)
)
runtime_wrap2(cls1, "clsmethod1", wrapper)
言い換えると、メタクラスで定義されたメソッドがインスタンスから可視でclassmethod
オブジェクトが可視ではないという重要な違いは別として、実行時のその他の違いは、あいまいで意味がないように見えますが、それは言語が移動する必要がないために起こりますクラスメソッドの特別なルールの外に:言語設計の結果として、クラスメソッドを宣言する両方の方法が可能です。1つは、クラス自体がオブジェクトであるという事実であり、もう1つは、多くの場合の可能性です。インスタンスとクラスの属性アクセスを特殊化できる記述子プロトコルの使用:
classmethod
組み込みは、ネイティブコードで定義されているが、それはただ純粋なPythonでコーディングすることができ、正確に同じ方法で動作します。以下の5行のクラスclassmethod
は、組み込みの@classmethod" at all (though distinguishable through introspection such as calls to
isinstance , and even
repr`とランタイムに違いがないデコレータとして使用できます):
class myclassmethod:
def __init__(self, func):
self.__func__ = func
def __get__(self, instance, owner):
return lambda *args, **kw: self.__func__(owner, *args, **kw)
また、メソッドを超え@property
て、メタクラスののような特殊化された属性がまったく同じように特殊化されたクラス属性として機能し、驚くべき動作をまったく行わないことを覚えておくことは興味深いです。