Pythonでの動的クラス定義

動的オブジェクト定義は、実行時の定義として理解できます。クラスの使い慣れたキーワード定義で使用される静的定義とは異なり、class動的定義はインラインクラスを使用しtypeます。



タイプメタクラス



タイプクラスは、オブジェクトのタイプを取得するためによく使用されます。たとえば、次のようになります。



h = "hello"
type(h)
<class 'str'>


しかし、他の用途もあります。新しいタイプを初期化できます。ご存知のように、Pythonのすべてはオブジェクトです。したがって、すべての定義には、クラスやオブジェクトを含むタイプがあります。例えば:



class A:
    pass
type(A)
<class 'type'>


typeインスタンスではなく 、クラスにクラスタイプが割り当てられる理由が完全に明確でない場合があります。



a = A()
type(a)
<class '__main__.A'>


オブジェクトにaは、タイプとしてクラスが割り当てられますこれは、インタプリタがオブジェクトをクラスのインスタンスとして扱う方法です。クラス自体はtype、基本クラスから継承するため、クラスタイプがありますobject



A.__bases__
(<class 'object'>,)


クラスタイプobject



type(object)
<class 'type'>


このクラスはobject、デフォルトですべてのクラスに継承されます。つまり、次のようになります。



class A(object):
    pass


と同じ:



class A:
    pass


定義されているクラスは、基本クラスを型として継承します。ただし、これは基本クラスobjectがクラスタイプである理由を説明していませんtype重要なのは、それtypeはメタクラスです。ご存知のように、すべてのクラスobjectは、メタクラスタイプの基本クラスを継承しますtypeしたがって、メタクラス自体を含め、すべてのクラスにもこのタイプがありますtype



type(type)
<class 'type'>


これは、Pythonの「入力のエンドポイント」です。型継承チェーンはクラスで閉じられtypeます。メタクラスtypeは、Pythonのすべてのクラスのベースとして機能します。これを確認するのは簡単です:



builtins = [list, dict, tuple]
for obj in builtins:
    type(obj)
<class 'type'>
<class 'type'>
<class 'type'>


クラスは抽象データ型であり、そのインスタンスには型としてクラス参照があります。



タイプクラスで新しいタイプを初期化する



タイプをチェックするとき、クラスはtype単一の引数で初期化されます。



type(object) -> type


そうすることで、オブジェクトのタイプを返します。ただし、クラスは3つの引数を使用して異なる初期化メソッドを実装し、新しいタイプを返します。



type(name, bases, dict) -> new type


タイプ初期化パラメーター



  • name

    新しいクラス(タイプ)の名前を定義する文字列。
  • bases

    基本クラス(新しいクラスが継承するクラス)のタプル。
  • dict

    将来のクラスの属性を持つ辞書。通常、キーに文字列があり、値に呼び出し可能なタイプがあります。


動的クラス定義



新しいタイプのクラスを初期化し、必要なすべての引数を指定して、次のように呼び出します。



MyClass = type("MyClass", (object, ), dict())
MyClass
<class '__main__.MyClass'>


通常どおり、新しいクラスで作業できます。



m = MyClass()
m
<__main__.MyClass object at 0x7f8b1d69acc0>


さらに、このメソッドは通常のクラス定義と同等です。



class MyClass:
    pass


クラス属性を動的に定義する



空のクラスにはほとんど意味がないので、疑問が生じます:属性とメソッドを追加する方法は?



この質問に答えるには、初期初期化コードを検討してください。



MyClass = type(“MyClass”, (object, ), dict())


通常、属性は初期化段階で3番目の引数(辞書)としてクラスに追加されます。辞書で属性の名前と値を指定できます。たとえば、次のような変数にすることができます。



MyClass = type(“MyClass”, (object, ), dict(foo=“bar”)
m = MyClass()
m.foo
'bar'


動的メソッド定義



メソッドなど、呼び出し可能なオブジェクトを辞書に渡すこともできます。



def foo(self):
    return “bar”
MyClass = type(“MyClass”, (object, ), dict(foo=foo))
m = MyClass()
m.foo
'bar'


このメソッドには1つの重大な欠点があります。それは、メソッドを静的に定義する必要があることです(メタプログラミングタスクのコンテキストでは、これは欠点と見なすことができます)。さらに、selfクラス本体の外部のパラメーターを使用してメソッドを定義すると、奇妙に見えます。したがって、属性のないクラスの動的初期化に戻りましょう。



MyClass = type(“MyClass”, (object, ), dict())


空のクラスを初期化した後、動的に、つまり明示的な静的定義なしで、クラスにメソッドを追加できます。



code = compile('def foo(self): print(“bar”)', "<string>", "exec")


compileソースコードをオブジェクトにコンパイルする組み込み関数です。コードは、関数exec()またはによって実行できますeval()



関数パラメーターのコンパイル



  • ソース

    ソースコード。モジュールへのリンクにすることができます。
  • filenameオブジェクトがコンパイルされるファイル

    の名前。
  • mode

    指定されている"exec"場合、関数はソースコードをモジュールにコンパイルします。


作業の結果はcompileクラスオブジェクトcodeです:



type(code)
<class 'code'>


オブジェクトをcodeメソッドに変換する必要があります。メソッドは関数であるため、クラスcodeオブジェクトをクラスオブジェクトに変換することから始めますfunctionこれを行うには、モジュールをインポートしますtypes



from types import FunctionType, MethodType


MethodType関数をクラスメソッドに変換するために後で必要になるため、 インポートします。



function = FunctionType(code.co_consts[0], globals(), “foo”)


FunctionType初期化メソッドのパラメーター



  • code

    クラスオブジェクトcodeクラスcode.co_consts[0]記述子の呼び出しです。これは、オブジェクトのコードに定数を持つタプルです。クラスメソッドとして追加しようとしている単一の関数を持つモジュールとしてオブジェクト想像してみてくださいモジュール内の唯一の定数であるため、そのインデックスです。co_constscodecode0
  • globals()

    グローバル変数の辞書。
  • name

    関数の名前を指定するオプションのパラメーター。


結果は次の関数です。



function
<function foo at 0x7fc79cb5ed90>
type(function)
<class 'function'>


次に、この関数をクラスメソッドとして追加する必要がありますMyClass



MyClass.foo = MethodType(function, MyClass)


関数をクラスメソッドに割り当てる非常に単純な式MyClass



m = MyClass()
m.foo()
bar


警告



99%の場合、静的クラス定義でうまくいくことができます。ただし、メタプログラミングの概念は、Pythonの内部を明らかにするのに適しています。私の実践ではそのような場合がありましたが、おそらく、ここで説明されている方法のアプリケーションを見つけるのは難しいでしょう。



動的オブジェクトを操作したことがありますか?多分他の言語で?



リンク






All Articles