1. ホーム
  2. python

[解決済み] 基本クラスから派生クラスを動的に作成する方法

2022-09-10 16:16:45

質問

例えば、以下のようなベースクラスがあります。

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')

新しいクラスを現在のスコープに置く関数呼び出しによって、これらのクラスを動的に作成するための素敵な、pythonicな方法はありますか、例えば。

foo(BaseClass, "My")
a = MyClass()
...

なぜこれが必要なのか、コメントや質問があることでしょう。派生クラスはすべてまったく同じ内部構造を持っていますが、違いはコンストラクタが以前に定義されていないいくつかの引数を取ることです。ですから、たとえば MyClass はキーワードを取ります。 a クラスのコンストラクタは TestClassbc .

inst1 = MyClass(a=4)
inst2 = MyClass(a=5)
inst3 = TestClass(b=False, c = "test")

しかし、クラスの型を入力引数として使うことは決してあってはなりません。

inst1 = BaseClass(classtype = "My", a=4)

私はこれを動作させることができましたが、他の方法、すなわち、動的に作成されたクラスオブジェクトを希望します。

どのように解決するのですか?

このコードのビットを使用すると、新しいクラスを動的な 名前とパラメータ名を持つ新しいクラスを作成することができます。 でのパラメータ検証は __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では良い習慣とは見なされていません。 変数名、またはデータ - そして実行時に学習した名前は、変数名"データ" よりも は、quot;変数"よりも "データ"です - 。

だから、クラスを辞書に追加して、そこから利用すればいいのです。

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 属性) にクラス名を設定します。

update, tl;dr: この回答は人気がありましたが、質問本文に非常に特化しています。一般的な回答は ベースクラスから派生クラスを動的に作成する方法に関する一般的な回答です。 を呼び出すだけです。 type を呼び出すだけです。新しいクラスの名前、ベースクラスを含むタプル、そして __dict__ のような新しいクラスのボディを渡します。

>>> new_class = type("NewClassName", (BaseClass,), {"new_method": lambda self: ...})

更新

これが必要な人は ディル プロジェクトもチェックしてみてください。これは、通常のオブジェクトに対して行うpickleのように、クラスのpickleとunpickleができると主張しており、私のテストのいくつかでそれを実行しました。