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_consts
code
code
0
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の内部を明らかにするのに適しています。私の実践ではそのような場合がありましたが、おそらく、ここで説明されている方法のアプリケーションを見つけるのは難しいでしょう。
動的オブジェクトを操作したことがありますか?多分他の言語で?