メタクラス
以前の説明でクラスで生成するインスタンスオブジェクトはobjectクラスを継承していると説明しましたが、クラスそのものはtypeクラスになります。
code: python
>> class MyClass:
... pass
...
>> obj = MyClass()
>> type(obj)
<class '__main__.MyClass'>
>> type(MyClass)
<class 'type'>
>>
組み込み関数type() はオブジェクトの型を返すものです。
クラスオブジェクト: クラス定義を評価されると生成されるオブジェクト
インスタンスオブジェクト : クラスオブジェクトから生成されるオブジェクト
code: python
>> for t in int, float, list, tuple, dict, set:
... print(t, type(t))
...
<class 'int'> <class 'type'>
<class 'float'> <class 'type'>
<class 'list'> <class 'type'>
<class 'tuple'> <class 'type'>
<class 'dict'> <class 'type'>
<class 'set'> <class 'type'>
>>
>> print(type(type))
<class 'type'>
typeクラス自身も typeクラスです。つまり、Pythonのすべてクラスはtypeクラスのインスタンスになり、typeクラスはメタクラスとなります。
メタクラスと他のPythonクラスの違いは次の通りです。
メタクラスは type クラスを継承するクラス
メタクタスはクラス定義でmetaclass=クラス名で指定する
metaclassを使用するclass文が評価されると、メタクラスが自動的に呼び出される
クラス定義で metaclassキーワードで指定されたクラスが呼び出され、metaclassキーワードで指定されない場合は、type.__call__()が呼び出されます。
次のコードは簡単なメタクラスを定義した例です。
__new__()メソッドに引数の内容を出力し、type.__ new__() を呼び出して、その結果を返しているだけのものです。
code: 0807_metaclass_sample.py
class SampleMeta(type):
def __new__(cls, clsname, superclasses, attributedict):
print("clsname: ", clsname)
print("superclasses: ", superclasses)
print("attributedict: ", attributedict)
return type.__new__(cls, clsname, superclasses, attributedict)
class Parent:
pass
# メタクラスはクラス定義が評価される段階で呼び出される
class Child(Parent, metaclass=SampleMeta):
pass
この例のクラスChild からは何もインスタンスオブジェクトは生成されていませんが、クラス定義が読み込まれて評価された段階でmetaclassで指定したSampleMetaクラスの__new__() が呼び出されているのがわかります。
次の例は、クラスChildの定義で、メタクラスSampleMetaのアトリビュートname を持つことがわかります。
code: 0808_metaclass.py
class SampleMeta(type):
def __new__(cls, clsname, superclasses, attributedict):
x = super().__new__(cls, clsname, superclasses, attributedict)
x.name = 'Python'
return x
class Parent:
pass
class Child(Parent, metaclass=SampleMeta):
pass
print(Child.name)
次のコードは、クラスとメタクラスでそれぞれ__init__()メソッドでアトリビュートname を初期化している例です。
code: 0809_metaclass_init.py
# オブジェクトの初期化
class MyClass:
def __init__(self):
self.name = 'Python'
x = MyClass()
y = MyClass()
print(x.name, y.name)
# クラスの初期化
class MyMeta:
def __init__(cls, clsname, superclasses, attributedict):
cls.name = 'Python'
class XClass(metaclass=MyMeta):
pass
class YClass(metaclass=MyMeta):
pass
print(XClass.name, YClass.name)
クラスとメタクラスの用法の違いを簡単にまとめてみましょう。
メタクラス: クラスオブジェクトの初期化
クラス: インスタンスオブジェクトの初期化
型の動的作成
type() は3つの引数を受けつけます。
type(clsname, bases, attrdict)
clsname: はクラス名を指定、クラスの__name__属性になる
bases: クラスが継承するベースクラスのタプルで指定、クラスの__bases__属性になる
attrdict: クラス本体の定義を含む名前空間を辞書で指定。 クラスの__dict__属性になる
通常は与えたclsname で与えたクラス名の型を返しますが、引数を3つ与えるとtypeクラスのインスタンスを生成します。つまり、新しい型を動的に作成します。
通常クラス定義は次のようにclass文を使します。
code: 0810_class_definition_static.py
class MyClass:
pass
obj = MyClass()
print(obj)
これを、組み込み関数type() を使って動的にクラスを生成してみましょう。
code: 0811_class_definition_dynamic.py
MyClass = type('MyClass', (), {})
obj = MyClass()
print(obj)
次の例は、クラス継承やアトリビュートを設定したものです。
code: 0812_class_definition_dynamic_with_inheritance.py
Parent = type('Parent', (), {})
Child = type('Child', (Parent,), dict(name='Python'))
obj = Child()
print(obj.name)
この例では、name変数をアトリビュートとしているだけですが、
関数名=関数のオブジェクトID のようにすればメソッドになります。
実際のところ、独自のメタクラスを作成することはほとんどないかもしれません。それでも、メタクラスを理解することは非常に有益で、クラス全般の理解を助けてくれます。
参考