はじめに

クラスはうまく定義できるようになりましたか?

前回、デコレーターで複数の関数に共通の処理を挿入する方法をお伝えしました。

同様に、クラスにも同じメソッドを持たせたいことがあります。
もちろん、サブクラスという方法もあるのですが、まったく無関係なクラスをサブクラスとして派生させるわけにはいきません。
このような場合、Pythonでは「メタクラス」という仕組みを利用できます。

この記事では、Python初心者の方向けに、Pythonのメタクラスの定義方法と使い方についてお伝えしていきます。
やや高度な内容ですが、ひとつずつしっかり理解していきましょう。

Pythonにおけるメタクラスとは?

以前の記事で、Pythonのクラスはオブジェクトであるということをお伝えしました。

type関数を使うことで、クラスを動的に作成することができます。

Pythonには、もうひとつクラスを作成するための機能があります。それが「メタクラス」です。

メタクラスは、クラスのクラスという位置づけであり、クラスオブジェクトの生成過程に干渉できます。
つまり、動的にメソッドを追加するなど、さまざまな拡張を行うことができるのです。

まずは、基本的な定義方法と使い方からみていきましょう。

メタクラスの基本的な定義方法と使い方

メタクラスの定義方法は、通常のクラス定義と変わりありませんが、前回紹介したデコレーターの場合と同様に既定の引数を用意する必要があります。
たとえば、次のような形です。

例:

def extra(self):
      print("追加されたメソッド")
class MyMetaClass(type):
      def __init__(cls, classname, superclases,
attributes):
           cls.extra = extra

 

この例では、MyMetaClassというメタクラスを定義しています。

メタクラスは、typeクラスのサブクラスにする必要があります。

__init__メソッドには、第一引数にクラスオブジェクト、第二引数にクラス名、第三引数に親クラス、第四引数に属性が渡されてくるため、ここではクラスにextraメソッドを追加しています。

このメタクラスを使うには、次のようにします。

例:

class MyClass(metaclass=MyMetaClass):
    pass
myclass = MyClass()
myclass.extra()

 

メタクラスを指定するには、クラス定義に”(metaclass=[メタクラス名])”を記述します。

MyClassクラスは空であり、なんのメソッドも持っていませんが、メタクラスによりextraメソッドが追加されているのが確認できます。
次項で、具体的な活用方法をみていきましょう。

メタクラスでシングルトンを実装してみよう

シングルトンはデザインパターンの一種で、クラスが唯一のオブジェクトしか持たないことを保証するものです。
つまり、クラスのインスタンスをひとつに制限します。

これは、複数のインスタンスを持つ必要がないクラスに便利です。
これをメタクラスで実現してみましょう。
たとえば、次のような実装が考えられます。

例:

class Singleton(type):
      def __init__(cls, classname, superclases,
attributes):
           cls.instance = None
      def __call__(cls, *args, **kwargs):
            if cls.instance is None:
                cls.instance = super(Singleton,
cls).__call__(*args, **kwargs)
           return cls.instance
class SingletonClass(metaclass=Singleton):
      pass
instance1 = SingletonClass()
instance2 = SingletonClass()
if instance1 == instance2:
    print("同じインスタンスです")
else:
      print("異なるインスタンスです")
print(id(instance1))
print(id(instance2))

 

クラスがインスタンス化されるときには、メタクラスの__call__メソッドが呼ばれるので、そこでインスタンス生成に干渉します。
つまり、まだインスタンスが生成されていなければ新しく作成し、すでに生成されていれば既存のインスタンスを返します。

super関数を使って、親クラス(ここではtype)の__call__メソッドを呼び出していることに注目して下さい。
この呼び出しは、通常のインスタンス生成で行われる処理と同等です。

実際にSingletonClassを2つ作成してみると、どちらも同じインスタンスであることが確認できます。
このように、Pythonではクラスを非常に柔軟にカスタマイズできます。

まとめ

メタクラスの使い方が理解できましたか?

メタクラスはサブクラスと似ているような気がしますが、役割は異なっています。
関連性を持つクラスはサブクラスとして実装し、互いに無関係なクラスに同じ機能をもたせたい場合にメタクラスを検討してみましょう。

うまく使いこなせればかなり強力な機能です。Pythonが動的であることを強く感じさせる機能といえるでしょう。