ビルダーパターンを使用して、さまざまな構成の可能性を分離しています。基本的に、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を追加するときに少し変更を加えたいと思います。
これがあなたが望むものであるかどうかはわかりませんが、文字列にリストされたクラスの束をインスタンス化するより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>]
Eval()を避けたい場合は、次のようにします。
id = "1234asdf"
constructor = globals()[id]
instance = constructor()
クラスが現在のスコープで定義されている(またはインポートされている)場合。
決してあなたがそれを助けることができるなら、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()
受け入れられた答え と 受け入れられた答えのコメント をまとめることにしました。これは、オーバーロードされた__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:エラー処理に追加しました。
Edited: permissive Creative Commons license の下に配置されています。楽しい。
あなたの質問に欠けているものがあるので、省略されたものを推測せざるを得ません。質問を編集して、漏れを修正してください。
_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であるという事実にもかかわらず、セキュリティホールです。評価(およびexec
とexecfile
)は、誰かが悪意のあるユーザーにアクセスを明確に許可した場合にのみ問題になります。