ホーム >  Python >  Pythonのメタクラス

投稿日:   |  最終更新日:

Pythonのメタクラス

Python

Pythonのメタクラスの基本的な使い方を調べてみます。

メタクラスとは?

メタクラスとは、インスタンス(実体)がクラス(設計図)となるクラス(設計図)のことです。例えばPythonでは、クラスオブジェクトの型がメタクラスにあたります。

クラスオブジェクトとは、クラス定義を抜けると生成されるオブジェクトです。

代表的なメタクラス

Pythonの組み込み(ビルトイン)クラスである「type」は、メタクラスです。

typeビルトイン関数は、型を調べるほかにクラスオブジェクトのインスタンス作成(コンストラクタ)としても利用されます。

例)typeは、以下のように動作確認できます。

>>> class A(object):
...     test = 1
... 
>>> A = type('A', (object,), {'test':1})
>>> 
>>> a = A()
>>> a.test
1

Pythonにおけるtypeを簡単にまとめると、以下のようになります。

  • class文はtype呼び出しのシンタックスシュガー(読みやすくした構文)
  • typeはデフォルトのメタクラス

typeはどこ?

そもそもPythonのクラスは、全てオブジェクトです。インスタンスはもちろん、インスタンスのもとになるクラスもオブジェクトです。全てのオブジェクトは必ず「型」を持ちます。

そして、 クラスはtypeのインスタンスです 。

例)文字列’abc’の型は以下のようになります。

>>> type('abc')
<type 'str'>

str型です。

この例では、「str」がクラスオブジェクトとなります。では、クラスオブジェクト「str」の型はどうなるか?以下のようにして調べます。

>>> type(str)
<type 'type'>

type型でした。

目に見えなくてもtypeが存在します。このように、クラスオブジェクトの型の「type」をメタクラスと呼びます。

メタクラスを何に使うか?

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

環境

Python 3.6

メタクラスの基本的な使い方

メタクラスの定義方法は、通常のクラス定義と変わりありません。ただし、特定の引数を用意する必要があります。

たとえば、次のような形です。

例)以下のようにMyMetaClassというメタクラスを定義しています。

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

関数extraの中にメタクラスMyMetaClassが存在します。

__init__メソッド

__init__メソッドには、以下の引数が渡されます。

第一引数 クラスオブジェクト:cls
第二引数 クラス名:classname
第三引数 親クラス:superclases
第四引数 属性:attributes

メタクラスを使う

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

>>> class MyClass(metaclass=MyMetaClass):
>>>     pass
... 
>>> myclass = MyClass()
>>> myclass.extra()
追加されたメソッド

解説

メタクラスを指定するには、クラス定義に「(metaclass=[メタクラス名])」を記述します。この例ではMyClassクラス自体は空であり、なんのメソッドも持っていません。

class MyClass(metaclass=MyMetaClass):
    pass

メタクラスによりextraメソッドが追加されているのが確認できます。

myclass = MyClass()
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

メタクラスを使ったクラス「SingletonClass」からインスタンスを作ります。

>>> ins1 = SingletonClass() 
>>> ins2 = SingletonClass()

同じインスタンスか判定します。

>>> if ins1 == ins2:
...     print("同じインスタンスです")
... else:
...     print("異なるインスタンスです")
... 
同じインスタンスです

インスタンスのidを表示してみます。

>>> print(id(ins1))
140534320890824
>>> print(id(ins2))
140534320890824

解説

...     def __call__(cls, *args, **kwargs):
...         if cls.instance is None:
...             cls.instance = super(Singleton, cls).__call__(*args, **kwargs)
...         return cls.instance

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

...             cls.instance = super(Singleton, cls).__call__(*args, **kwargs)

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

if ins1 == ins2:
    print("同じインスタンスです")
else:
    print("異なるインスタンスです")

同じインスタンスです

print(id(ins1))
140534320890824
print(id(ins2))
140534320890824

実際にSingletonClassを2つ作成してみると、どちらも同じインスタンスであることが確認できます。


「class Meta」はメタクラスではない

WEBフレームワークのDjangoでは、以下のような記述をする時があります。

class Question(models.Model):
    class Meta:
        verbose_name = '質問'
        verbose_name_plural = '質問の複数形'

まぎらわしいですが、これはメタクラスではなくメタデータです。

モデルのメタデータ

Djangoでは、モデルのメタデータを「Meta」という名前のクラスでモデルの中に作ります。モデルのメタデータとは、「フィールド定義じゃない何か」です。並べる時の順番指定(ordering)や、DBのテーブル名や(db_table)、表示名の単数形と複数形(verbose_name と verbose_name_plural)などを指定します。つまり、モデルをデコレーションします。(※Metaは必須ではありません。)

まとめ

  • メタクラスとは、インスタンスがクラスとなるクラスのことです。
  • メタクラスは、関係のない別々のクラスに同じメソッドを持たせたい時に使います。
  • メタクラスを定義するには、特定の引数を用意する必要があります。
  • Pythonでは、特に指定がなければtypeがメタクラスです。
  • 新しくメタクラスを追加する場合、typeクラスのサブクラスにする必要があります。


トラックバック用のURL
プロフィール

名前:イワサキ ユウタ 職業:システムエンジニア、ウェブマスター、フロントエンドエンジニア 誕生:1986年生まれ 出身:静岡県 特技:ウッドベース 略歴 20

最近の投稿
人気記事
カテゴリー
広告