我正在使用构建器模式来分离一堆不同的配置可能性。基本上,我有一堆名为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添加到IDS列表中,而不是在整个代码中添加新的ID。
根据数据的来源,看起来有一些不同的意见。这些ID输入到其他人无法访问的文件中。我不是从命令行读取字符串,并且我希望将来在添加新ID时能够进行少量更改。
答案 0 :(得分:55)
如果你想避免使用eval(),你可以这样做:
id = "1234asdf"
constructor = globals()[id]
instance = constructor()
前提是该类在当前范围内定义(或导入)。
答案 1 :(得分:17)
不确定这是你想要的,但它似乎是一种更加Pythonic的方式来实例化字符串中列出的一堆类:
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>]
答案 2 :(得分:9)
从不使用eval()
,如果可以提供帮助的话。 Python有所以许多更好的选项(调度字典,getattr()
等),你永远不应该使用称为eval()
的安全漏洞。
答案 3 :(得分:5)
最简单的方法就是创建一个词典。
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()
答案 4 :(得分:1)
我决定将accepted answer和comment on the accepted answer.放在一起,我还添加了重载的__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"))
已编辑:我添加了一些错误处理。
已编辑:位于permissive Creative Commons license下。享受。
答案 5 :(得分:0)
你的问题中缺少一些东西,所以我不得不猜测遗漏的东西。随意编辑您的问题以纠正遗漏。
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()
绝对是而不是安全漏洞。如果有人专门授予恶意用户访问权限,则Eval(以及exec
和execfile
)只是一个问题。