Pythonのメタクラスの作り方を分かりやすく解説する

スポンサーリンク

メタクラスは、クラスのクラスです。

クラスのインスタンス(オブジェクト)が生成されるたびに、そのオブジェクトの振る舞いがクラスによって定義されます。

メタクラスは、クラス自体の振る舞いを定義します。

スポンサーリンク

Pythonにおけるメタクラスの使用法

メタクラスを使用する背景には、PythonのクラスがObjectsそのものであることがあります。

クラスはObjectsなので、変数に代入したり、コピーしたりと、様々な操作をすることができます

また、Objectである以上、他のObjectと同様に動的に生成することも可能です。

メタクラスの概念をよりよく理解するために、まずPythonがどのようにクラスを定義しているかを見てみましょう。

この言語では、intstringなど、あらゆるものをオブジェクトとして定義しています。

Pythonのオブジェクトの型を見るには、 type 関数を使います。

>>> print(type(123))
<class 'int'>
>>> print(type([1, 2, 3]))
<class 'list'>
 
>>> class A():
...     def __init__(self):
...             pass
...
>>> a = A()
>>> print(type(a))
<class '__main__.A'>

この関数は、それぞれのケースに対して class を返します。

しかし、Pythonがクラス自体をどのように定義しているかを見るには、単にその型を見ればいいのです。

>>> print(type(type(123))
<class 'type'>
>>> print(type(A))
<class 'type'>

見ての通り、type(class) はクラスの type です。

これはメタクラスという概念で、他のクラスを定義する役割を担っています。

基本的にはクラスファクトリで、ここから ints や strs などの他のクラスを定義することができるのです。

>>> Animal = type('Animal', (), dict(__init__ = lambda self: None, worth = lambda self, value: value))

type` は、言語がオブジェクトを作成するために使用するメタクラスです。

(このため、すべてのオブジェクトは型を持っています。

そして、typeはメタクラスであるため、そこから他のクラスを作ることができる。

クラスを動的に作成する

クラスを動的に作成するには、コンストラクタ type(name, bases, attr) からインスタンス化します。

  • name -> クラスの名前
  • bases -> 新しいクラスが継承するクラス名
  • attr -> クラスに含まれる属性とメソッドの辞書
class Animal():
    def __init__(self):
        pass
     
    def worth(self, value):
        return value

と同じです。

class MyMetaclass(type):
    def __new__(cls, name, bases, dict):
        print('Creating a new object of', name)
        # Invoke __new__() method of the metaclass type
        return super(MyMetaclass, cls).__new__(cls, name, bases, dict)
 
    def __init__(cls, name, bases, dict):
        print('Initialising class', name)
        super(MyMetaclass, cls).__init__(name, bases, dict)

最初のコードは、2番目のコードと比較して、はるかに簡単に書くことができます。

動的な宣言であっても、クラス本体を記述することは、あまり柔軟性を提供しません。

したがって、メタクラスは新しいクラスを動的に作成するための強力かつ簡単な方法を提供します。

カスタムメタクラスの作成

独自のメタクラスを作成するには、既存の type メタクラスを継承し、いくつかの特別なメソッドをオーバーライドする必要があります。

  • __init__() の前に呼び出されます。オブジェクトを作成し、それを返します。
  • _init__() -> これは新しく作成されたオブジェクトを初期化するもので、パラメータ(selfパラメータ)として渡されま す。

次のスニペットは、メタクラスを作成する方法を示しています。

class Student(metaclass=MyMetaclass):
    def __init__(self, name):
        self.name = name
 
    def get_name(self):
        return self.name

さて、カスタムのメタクラスを作成したので、このメタクラスを使用する他のクラスを作成する必要があります。

そのためには、新しいクラス定義で metaclass パラメータを渡します。

これは、クラスが type ではなく、独自のメタクラスとして私たちのカスタム・メタクラスを使用するように指示するものです。

stud = Student('Amit')
print(stud.get_name())
print('Type of Student object:', type(stud))
print('Type of Student Class:', type(Student))

ここでは、 StudentMyMetaclass をメタクラスとして使用します。

したがって、Student のインスタンスを作成するときには、 type メタクラスではなく、独自のメタクラスのメソッドが呼び出されます。

Creating a new object of Student
Initialising class Student
Amit
Type of Student object: <class '__main__.Student'>
Type of Student Class: <class '__main__.MyMetaclass'>

結果は以下の通りです。

Metaclass
Metaclass hierarchy

注:古いバージョンのPython 2.7以下では、使用するメタクラスを指定するために __metaclass__ キーワードを使用しています。

Python3では、この挙動を変更して、 metaclass をパラメータとして渡すようにしました。

まとめ

メタクラスはカスタムAPIを作成したり、オブジェクトやクラス作成時の振る舞いを定義するための非常に強力な方法として機能しますが、同じことを行うための他の回避方法があるため、実際にはほとんど使用されることはありません。

この記事は、Pythonがどのように type メタクラスの観点からすべてを定義しているかを理解するための出発点としての役割しか果たしません。

メタクラスがどのように作成され、クラスが作成される際に呼び出されるメソッドについて見てきました。

参考文献

StackOverflow post on Metaclasses (このトピックに関する詳細なディスカッションを提供しています。

タイトルとURLをコピーしました