Python PR

【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で動的に型を生成する方法を解説しました。

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

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