web-dev-qa-db-ja.com

文字列を使用してクラスをインスタンス化できますか?

ビルダーパターンを使用して、さまざまな構成の可能性を分離しています。基本的に、ID(ID12345のようなもの)という名前のクラスがたくさんあります。これらはすべて、基本ビルダークラスから継承します。私のスクリプトでは、このアプリを実行するたびに各クラス(約50)のインスタンスをインスタンス化する必要があります。だから、私は次のようなことをする代わりに、

ProcessDirector = ProcessDirector()
ID12345 = ID12345()
ID01234 = ID01234()

ProcessDirector.construct(ID12345)
ProcessDirector.construct(ID01234)

ID12345.run()
ID01234.run()

私はこのようなことをすることができます(私はこれが機能しないことを知っています):

IDS = ["ID12345", "ID01234"]

ProcessDirector = ProcessDirector()
for id in IDS:
  builder = id() #some how instantiate class from string
  ProcessDirector.construct(builder)
  builder.run()

そうすることで、将来新しいIDを追加する必要がある場合、コード全体に新しいIDを追加するのではなく、IDSリストにIDを追加するだけで済みます。

編集

データの出所に基づいて、いくつかの異なる意見があるように見えます。これらのIDは、他の誰もアクセスできないファイルに入力されます。私はコマンドラインから文字列を読んでいないので、将来新しいIDを追加するときに少し変更を加えたいと思います。

36
scottm

これがあなたが望むものであるかどうかはわかりませんが、文字列にリストされたクラスの束をインスタンス化するよりPython的な方法のようです:

class idClasses:
    class ID12345:pass
    class ID01234:pass
# could also be: import idClasses

class ProcessDirector:
    def __init__(self):
        self.allClasses = []

    def construct(self, builderName):
        targetClass = getattr(idClasses, builderName)
        instance = targetClass()
        self.allClasses.append(instance)

IDS = ["ID12345", "ID01234"]

director = ProcessDirector()
for id in IDS:
    director.construct(id)

print director.allClasses
# [<__main__.ID12345 instance at 0x7d850>, <__main__.ID01234 instance at 0x7d918>]
17
dbr

Eval()を避けたい場合は、次のようにします。

id = "1234asdf"
constructor = globals()[id]
instance = constructor()

クラスが現在のスコープで定義されている(またはインポートされている)場合。

58
GoldenBoy

決してあなたがそれを助けることができるなら、eval()を使用してください。 Pythonにはsoがあります。多くのより良いオプション(ディスパッチ辞書、getattr()など)]で、eval()

最も簡単な方法は、辞書を作成することです。

class A(object): 
    pass
class B(object): 
    pass

namedclass = {'ID12345': A, 'ID2': A, 'B': B, 'AnotherB': B,  'ID01234': B}

次に、それを使用します(コード例):

IDS = ["ID12345", "ID01234"]

ProcessDirector = ProcessDirector()
for id in IDS:
    builder = namedclass[id]() 
    ProcessDirector.construct(builder)
    builder.run()
5
nosklo

受け入れられた答え受け入れられた答えのコメント をまとめることにしました。これは、オーバーロードされた__getitem__も追加したので、これは工場パターンのように見えます。

import sys
import traceback
import ipdb


class CarTypes:
    class Toyota:
        def __repr__(self):
            return "Toyota()"
        def __str__(self):
            return "Instance of Toyota() class"
    class Nissan:
        def __repr__(self):
            return "Nissan()"
        def __str__(self):
            return "Instance of Nissan() class"


class Car:
    def __init__(self):
        self._all_classes = {}

    def construct(self, builder_name):
        setattr(self, builder_name, CarTypes())
        try:
            target_class = getattr(CarTypes, builder_name)
            instance = target_class()
            self._all_classes[builder_name] = instance
        except AttributeError:
            print("Builder {} not defined.".format(builder_name))
            traceback.print_stack()

    def __getitem__(self, type_name):
        return self._all_classes[type_name]

    def car_type(self, type_name):
        return self._all_classes[type_name]


IDS = ["Toyota", "Nissan", "Unknown"]

director = Car()
for id in IDS:
    director.construct(id)

print(director["Toyota"])
print(director["Nissan"])
print(director.car_type("Toyota"))
print(director.car_type("Nissan"))

Edited:エラー処理に追加しました。

Editedpermissive Creative Commons license の下に配置されています。楽しい。

1
Daisuke Aramaki

あなたの質問に欠けているものがあるので、省略されたものを推測せざるを得ません。質問を編集して、漏れを修正してください。

_class ProcessDirector( object ):
    # does something

class ID12345( SomeKindOfProcess ):
    pass

class ID001234( SomeKindOfProcess ):
    pass

idList= [ID12345, ID01234]

theProcessDirector = ProcessDirector()
for id in idList:
  builder = id() #Instantiate an object from the class object
  theProcessDirector.construct(builder)
  builder.run()
_

これは非常にうまく機能します。文字列からインスタンス化することはありません。実際には、これはあまり必要ありません。時々ですが、めったにありません。より一般的には、インスタンスオブジェクトが必要なクラスオブジェクトのリストです。

実際にコマンドラインからクラス名を取得している場合は、次の小さな変更を加えます。

_validClasses = [ ID12345, ID01234 ]
validMap = dict( ( (c.__name__, c) for c in validClasses ) )
nameStrings = [ "ID12345", "ID01234" ] # from your command-line 
idList= [ validMap[s] for s in nameStrings ]
_

他のすべては同じままです。

[また、可能であれば、インスタンス変数名を小文字で開始してみてください。大文字で始まる名前は通常、クラス名です。]

編集

evalを削除しました。 eval()は絶対にnotであるという事実にもかかわらず、セキュリティホールです。評価(およびexecexecfile)は、誰かが悪意のあるユーザーにアクセスを明確に許可した場合にのみ問題になります。

0
S.Lott