Python
 Computer >> コンピューター >  >> プログラミング >> Python

【Python入門】メタクラスによるメタプログラミングの基本と実践例

メタプログラミングとは、プログラムが自分自身を操作したり、自身についての知識を持ったりするプログラミング手法のことを指します。Pythonでは「メタクラス」と呼ばれる特殊なクラスを使うことで、クラスに対するメタプログラミングを実現できます。

メタクラスによるメタプログラミングでは、既存のコードを修正・ラップ・生成することでコードを操作する関数やクラスを構築します。

メタプログラミングの主な機能は以下の通りです。

  • メタクラス
  • デコレータ
  • クラスデコレータ

メタクラスとは何か

メタクラスを簡潔に定義するなら、「クラスを作り出すクラス」です。Pythonはオブジェクト指向言語であり、すべてがオブジェクトとして扱われます。クラスも例外ではありません。classキーワードでクラスを定義すると、Pythonは実際にそのコードを実行し、オブジェクトを生成します。オブジェクトである以上、変数への代入やコピー、属性の追加、関数の引数としての利用などが可能です。メタクラスを使うことで、クラスのプロパティ変更や属性の妥当性チェックなど、クラス生成の過程そのものを制御できるようになります。

Python組み込みのメタクラス

すべてのクラスのメタクラスはtypeです。

class a:
    pass

print(type(a))

実行結果:

<class 'type'>

この実行結果から、クラス「a」がtypeというクラスのオブジェクト(インスタンス)であることが分かります。つまりtypeこそがメタクラスであり、Pythonではすべてのクラスが組み込みメタクラスtypeに属しています。

メタクラスを使ってPythonクラスを作成する

前述のとおり、デフォルトのメタクラスはtypeです。この標準メタクラスを使って、新しいクラスを作成することもできます。

>>> class hello(object):
    pass

上記のコードは、次のように手動でも作成できます。

>>> hello = type('hello', (), {})
>>> print(hello)  # クラスオブジェクトを返す

>>> print(hello())  # クラスからインスタンスを生成
<__main__.hello object at 0x05D86830>

typeはクラスの属性を定義するための辞書を受け取ります。したがって、

>>> class hello(object):
    world = True

このコードは以下と同等です。

hello = type('hello', (), {'world': True})

独自のメタクラスを作成する

それでは、どのような場合にメタクラスを作成すべきでしょうか。それは、クラスの属性検証や修正など、クラスの生成過程を制御したいときです。

クラスのインスタンス化プロセスは次の流れになります。

  • __new__()を呼び出してインスタンスを作成する
  • __init__()を呼び出して、作成されたインスタンスを初期化する

つまり、カスタムメタクラスを作成するときは、親クラスの__new__()または__init__メソッドを書き換えることになります。

例1:クラスの属性を変換するメタクラス

次の例では、クラス内で定義された属性名を自動的に小文字に変換するメタクラスを作成しています。

class LowercaseMeta(type):
    def __new__(mcs, name, bases, attrs):
        lower_attrs = {}
        for k, v in attrs.items():
            if not k.startswith('__'):
                lower_attrs[k.lower()] = v
            else:
                lower_attrs[k] = v
        return type.__new__(mcs, name, bases, lower_attrs)

class LowercaseClass(metaclass=LowercaseMeta):
    BAR = True
    def HELLO(self):
        print('hello')

print(dir(LowercaseClass))
LowercaseClass().hello()

実行結果

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'hello']
hello

実行結果を見ると、大文字で定義したBARHELLOが、それぞれ小文字のbarhelloに自動的に変換されていることが分かります。

例2:クラスに新しい属性を追加するメタクラス

次の例では、リストクラスにaddメソッドを自動的に追加するメタクラスを定義しています。

class ListMeta(type):
    def __new__(mcs, name, bases, attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(mcs, name, bases, attrs)

class MyList(list, metaclass=ListMeta):
    pass

l = MyList()
l.add(1)
print(l)

# 比較用:同じことをクラスデコレータで実現した場合
def class_decorator(cls):
    cls.add = lambda self, value: self.append(value)
    return cls

@class_decorator
class MyList(list):
    pass

l = MyList()
l.add(1)
print(l)

実行結果

[1]
[1]

このように、メタクラスとクラスデコレータは似たような目的を達成できますが、継承関係にあるサブクラスにも一括して適用したい場合などはメタクラスの方が適しています。

__metaclass__属性について

Pythonでは、クラス自身またはその基底クラスのいずれかに__metaclass__属性が定義されていれば、それがメタクラスとして使用されます。定義されていない場合は、デフォルトのtypeがメタクラスとなります。

たとえば、次のようにクラスを定義した場合を考えてみましょう。

class hello(object):
    x = 10

このhelloクラスには__metaclass__属性がないため、代わりにtypeが使用され、クラスの生成は次のように行われます。

hello = type(name, bases, dict)

一方、helloクラスにメタクラスが明示的に定義されている場合はどうなるでしょうか。

class hello(object):
    __metaclass__ = myMetaClass
    pass

この場合、クラスの生成にはtypeではなくmyMetaClassが使用されます。つまり、次のように処理されます。

hello = myMetaClass(name, bases, dict)

まとめ

メタクラスは強力な仕組みですが、その分複雑さも増します。日常的な開発ではデコレータで十分なケースが多いものの、フレームワークやライブラリ開発においてAPIの一貫性を保ちたい場合や、サブクラス全体に共通の振る舞いを強制したい場合には、メタクラスが非常に有効です。まずはtypeがすべてのクラスの基盤であるという事実を理解し、必要に応じてカスタムメタクラスの活用を検討してみてください。

  1. Pythonの継承とは?単一継承と階層継承の基本をサンプルコードで解説

    本記事では、Python 3.xにおける継承(インヘリタンス)とクラスの拡張方法について詳しく解説します。 継承とは、現実世界のモノや概念の関係性を自然に表現できる、オブジェクト指向プログラミングの中核となる仕組みです。継承を活用すると、次のようなメリットが得られます。 再利用性:すでに書いたコードを流用でき、重複を削減できる 推移性:クラス間の関係を連鎖的に引き継げる 開発速度の向上:ゼロから書かずに済むため、短期間で開発できる 保守性・拡張性:既存クラスを壊さずに機能を追加しやすい 継承の5つの種類 Pythonの継承は、その構造によって主に以下の5種類に分類されます。 単一継承(

  2. PythonのTurtleグラフィックス入門!基本メソッド一覧と図形描画サンプルコード集

    Turtle(タートル)はPythonに標準搭載された特殊な機能で、専用の描画ウィンドウ上に簡単に図形を描くことができるモジュールです。 使い方はとてもシンプルです。まずturtleモジュールをインポートし、次にウィンドウ(Screen)を作成、その後タートルオブジェクトを生成します。あとは各種メソッドを呼び出すだけで、キャンバス上に自由に描画できます。この記事では、よく使われるメソッドの一覧表と、正方形・星・六角形などの具体的な描画例をサンプルコード付きで解説します。 主なTurtleメソッド一覧 メソッド パラメータ 説明 Turtle() なし 新しいタートルオブジェクトを作成