たとえば、次のような基本クラスがあります。
class BaseClass(object):
def __init__(self, classtype):
self._type = classtype
このクラスから他のいくつかのクラスを派生します。例えば
class TestClass(BaseClass):
def __init__(self):
super(TestClass, self).__init__('Test')
class SpecialClass(BaseClass):
def __init__(self):
super(TestClass, self).__init__('Special')
次のように、新しいクラスを現在のスコープに配置する関数呼び出しによって、これらのクラスを動的に作成する優れた Python の方法はありますか?
foo(BaseClass, "My")
a = MyClass()
...
なぜこれが必要なのかというコメントや質問があるでしょう。派生クラスはすべて、コンストラクターが以前に定義されていない引数をいくつか取るという違いを除いて、まったく同じ内部構造を持っています。たとえば、 はMyClass
キーワードを取りますa
が、クラスのコンストラクターはと をTestClass
取ります。b
c
inst1 = MyClass(a=4)
inst2 = MyClass(a=5)
inst3 = TestClass(b=False, c = "test")
しかし、クラスの型を入力引数として使用してはいけません。
inst1 = BaseClass(classtype = "My", a=4)
私はこれを動作させましたが、他の方法、つまり動的に作成されたクラス オブジェクトの方が好みです。
ベストアンサー1
このコードを使用すると、動的な名前とパラメータ名を持つ新しいクラスを作成できます。 のパラメータ検証では__init__
不明なパラメータが許可されません。型や必須などの他の検証が必要な場合は、そこにロジックを追加するだけです。
class BaseClass(object):
def __init__(self, classtype):
self._type = classtype
def ClassFactory(name, argnames, BaseClass=BaseClass):
def __init__(self, **kwargs):
for key, value in kwargs.items():
# here, the argnames variable is the one passed to the
# ClassFactory call
if key not in argnames:
raise TypeError("Argument %s not valid for %s"
% (key, self.__class__.__name__))
setattr(self, key, value)
BaseClass.__init__(self, name[:-len("Class")])
newclass = type(name, (BaseClass,),{"__init__": __init__})
return newclass
これは、例えば次のように機能します。
>>> SpecialClass = ClassFactory("SpecialClass", "a b c".split())
>>> s = SpecialClass(a=2)
>>> s.a
2
>>> s2 = SpecialClass(d=3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 8, in __init__
TypeError: Argument d not valid for SpecialClass
命名スコープに動的な名前を挿入することを求めているようですが、それPython では良い習慣とはみなされません - コーディング時にわかっている変数名かデータのいずれかです - 実行時に学習される名前は「変数」というより「データ」です -
したがって、クラスを辞書に追加して、そこから使用することができます。
name = "SpecialClass"
classes = {}
classes[name] = ClassFactory(name, params)
instance = classes[name](...)
そして、あなたのデザインが名前をスコープ内に含めなければならない場合は、同じことをして、globals()
任意の辞書の代わりに呼び出します:
name = "SpecialClass"
globals()[name] = ClassFactory(name, params)
instance = SpecialClass(...)
(確かに、クラスファクトリ関数が呼び出し元のグローバルスコープに動的に名前を挿入することは可能ですが、これはさらに悪い習慣であり、Pythonの実装間で互換性がありません。これを行う方法は、呼び出し元の実行フレームを取得することです。sys._getframe(1)フレームのグローバル辞書のクラス名をそのf_globals
属性に設定します。
更新、要約:この回答は人気がありましたが、質問本文に非常に特化しています。一般的な回答は「基本クラスから派生クラスを動的に作成する」type
Python では、新しいクラス名、基本クラスを含むタプル、および__dict__
新しいクラスの本体を渡すだけの簡単な呼び出しです。次のようになります。
>>> new_class = type("NewClassName", (BaseClass,), {"new_method": lambda self: ...})
アップデート
これを必要とする人は、ディルプロジェクト - 通常のオブジェクトに対して pickle が行うのと同じように、クラスを pickle 化および unpickle 化できると主張しており、いくつかのテストではその通りでした。