質問はPython2.7に関するものであり、すでに優れた回答がありますが、Python 3.3についても同じ質問があり、このスレッドはGoogleで見つけた回答に最も近いものでした。私はPythonのドキュメントを掘り下げて、Python 3.xのより良い解決策を見つけました。そして、Python 3.xバージョンを探している他の人のために、私の発見を共有しています。
Python3.xでメタクラスに引数を渡す
Pythonの公式ドキュメントを調べたところ、Python 3.xには、欠陥がないわけではありませんが、メタクラスに引数を渡すネイティブメソッドが用意されていることがわかりました。
クラス宣言にキーワード引数を追加するだけです。
class C(metaclass=MyMetaClass, myArg1=1, myArg2=2):
pass
...そしてそれらは次のようにあなたのメタクラスに渡されます:
class MyMetaClass(type):
@classmethod
def __prepare__(metacls, name, bases, **kwargs):
#kwargs = {"myArg1": 1, "myArg2": 2}
return super().__prepare__(name, bases, **kwargs)
def __new__(metacls, name, bases, namespace, **kwargs):
#kwargs = {"myArg1": 1, "myArg2": 2}
return super().__new__(metacls, name, bases, namespace)
#DO NOT send "**kwargs" to "type.__new__". It won't catch them and
#you'll get a "TypeError: type() takes 1 or 3 arguments" exception.
def __init__(cls, name, bases, namespace, myArg1=7, **kwargs):
#myArg1 = 1 #Included as an example of capturing metaclass args as positional args.
#kwargs = {"myArg2": 2}
super().__init__(name, bases, namespace)
#DO NOT send "**kwargs" to "type.__init__" in Python 3.5 and older. You'll get a
#"TypeError: type.__init__() takes no keyword arguments" exception.
and (Python 3.5以前。「UPDATE」を参照)kwargs
の呼び出しを除外する必要があります。そうしないと、引数が多すぎるために例外が発生します。つまり、この方法でメタクラス引数を渡す場合は、常に実装し、カスタムキーワード引数が基本クラスとメソッドに到達しないようにする必要があります。 余分なキーワード引数を適切に処理しているようです(したがって、私が知らない機能がある場合に備えて、例でそれらを渡すのはなぜですか)。したがって、定義はオプションです。type.__new__
type.__init__
TypeError
MyMetaClass.__new__
MyMetaClass.__init__
type.__new__
type.__init__
type.__prepare__
**kwargs
type.__prepare__
アップデート
Python 3.6では、表示type
が調整され、type.__init__
追加のキーワード引数を適切に処理できるようになりました。それでも定義する必要がありますtype.__new__
(例外をスローしTypeError: __init_subclass__() takes no keyword arguments
ます)。
壊す
Python 3では、クラス属性ではなくキーワード引数を使用してメタクラスを指定します。
class MyClass(metaclass=MyMetaClass):
pass
このステートメントは、大まかに次のように解釈されます。
MyClass = metaclass(name, bases, **kwargs)
...ここmetaclass
で、は渡した「metaclass」引数の値、name
はクラスの文字列名('MyClass'
)、は渡した基本クラス(この場合はbases
長さゼロのタプル)、はキャプチャされていないキーワードです。引数(この場合は空)。()
kwargs
dict
{}
これをさらに分解すると、ステートメントは大まかに次のように解釈されます。
namespace = metaclass.__prepare__(name, bases, **kwargs) #`metaclass` passed implicitly since it's a class method.
MyClass = metaclass.__new__(metaclass, name, bases, namespace, **kwargs)
metaclass.__init__(MyClass, name, bases, namespace, **kwargs)
...クラス定義に渡したキャプチャされていないキーワード引数kwargs
は常にどこにありますか。dict
上記の例を分解すると、次のようになります。
class C(metaclass=MyMetaClass, myArg1=1, myArg2=2):
pass
...大まかに次のように変換されます。
namespace = MyMetaClass.__prepare__('C', (), myArg1=1, myArg2=2)
#namespace={'__module__': '__main__', '__qualname__': 'C'}
C = MyMetaClass.__new__(MyMetaClass, 'C', (), namespace, myArg1=1, myArg2=2)
MyMetaClass.__init__(C, 'C', (), namespace, myArg1=1, myArg2=2)
この情報のほとんどは、「クラス作成のカスタマイズ」に関するPythonのドキュメントからのものです。