你能用字符串实例化一个类吗?

时间:2009-02-16 16:00:27

标签: python design-patterns reflection

我正在使用构建器模式来分离一堆不同的配置可能性。基本上,我有一堆名为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时能够进行少量更改。

6 个答案:

答案 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 answercomment 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(以及execexecfile)只是一个问题。