Python

【Python】動的に型を生成する方法を解説

この記事では、Python で型を動的に生成する方法を解説します。

動的に型生成することでメタプログラミングをすることができます。具体的には、クラスの生成プロセスをカスタマイズすることができます。

それでは、型の生成方法を見ていきましょう!

動的に型を生成する方法

動的に型を生成するには type() を使います。そうです、いつも型を確認するために使っていたあの関数(実際はクラス)です。

type(name, bases, dict, **kwds)
name 生成するクラスの名前を指定する。
based 基底クラスをタプルで指定する。空の場合は object が指定される。
dict クラスの属性を指定する。

型を動的に生成してみる

それでは、試しに MyClass という簡単なクラスを動的に生成してみます。

# 動的に型を生成
MyClass = type('MyClass', (), {})
print(MyClass, type(MyClass))

# インスタンス化
mc = MyClass()
print(mc, type(mc))

実行結果

<class '__main__.MyClass'> <class 'type'>
<__main__.MyClass object at 0x104236fd0> <class '__main__.MyClass'>

動的に型を生成してインスタンス化することができました。

基底クラスの指定

第二引数にクラスを指定することで生成するクラスの親(基底クラス)を設定できる。親クラスはタプルで指定するが、空のタプルを指定すると object が設定される。

以下のコードでは、list を継承させたクラスを動的に生成しています。

# listを継承したクラスを動的に生成
MyList = type('MyList', (list,), {})
print(MyList, type(MyList))

# インスタンス化
ml = MyList([1, 2, 3])

# listの機能を使う
ml.append(10)
ml.remove(2)

print(ml)

実行結果

<class '__main__.MyList'> <class 'type'>
[1, 3, 10]

親クラスはタプルとして指定する必要があるため (list,) となっていることに注意。(list) だとタプルではなくなってしまう。

属性の指定

第三引数では、辞書で属性を指定することができる。属性にはクラス変数やメソッドなどを渡すことができる。ちなみに、__init__ などの特殊メソッドも指定可能である。

# 渡す用の __init__
def __init__(self, a):
    self.a = a

MyClass = type('MyClass', (), {'clsarg': 1, '__init__': __init__})

print(f'クラス変数 clsarg: {MyClass.clsarg}')

mc = MyClass(1)
print(f'インスタンス変数 a: {mc.a}')

実行結果

<class '__main__.MyClass'> <class 'type'>
クラス変数 clsarg: 1
インスタンス変数 a: 1

型オブジェクトはtypeのインスタンス

この記事では、動的に型を生成する方法を学んできましたが、実は通常のクラスも type() から生成されています。

実際に定義したクラスの型を確認してみます。

class MyClass:
    pass

print(type(MyClass))

実行結果

<class 'type'>

MyClass の型は type でした。

使い道

メタクラスを使ってクラス生成プロセスをカスタマイズする際に動的に型を生成する必要があります。

type を継承させたクラス内の __new__メソッド でクラスの生成方法を定義し、それを他のクラスに metaclass として渡します。そうすることでクラスオブジェクトがインスタンス化される際のプロセスを指定できます。

例えば以下のように使います。

  • STEP1
    メタクラスの準備
    クラスオブジェクトのインスタンス化の方法を定義したメタクラスの定義。この時に生成したクラスに属性を持たせたりすることができる。

    class MetaClass(type):
        def __new__(cls, *args, **kwargs):
            # 動的にクラスを生成する
            c = type.__new__(cls, *args, **kwargs)
    
            # どのタイミングで実行されるか確認するための出力
            print('MetaClass.__new__')
    
            # 生成したクラスを返す
            return c
  • STEP2
    メタクラスの指定
    クラスの metaclass にメタクラスを指定する。

    class MyClass(metaclass=MetaClass):
        pass

    実行結果

    MetaClass.__new__

    MetaClass.__new__メソッド に記述した print() が実行されたので MyClass を定義した時点で指定したメタクラスを使ってクラスオブジェクトが生成されたのがわかる。

このように、クラスの生成自体をカスタマイズすることができます。

まとめ

この記事では、Python で動的に型を生成する方法を解説しました。

動的に型を生成することでメタプログラミングをすることができますが、扱いが難しいので注意して使いましょう!

それでは今回の内容はここまでです。ではまたどこかで〜( ・∀・)ノ