简体   繁体   English

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

[英]Can you use a string to instantiate a class?

I'm using a builder pattern to seperate a bunch of different configuration possibilities. 我正在使用构建器模式来分离一堆不同的配置可能性。 Basically, I have a bunch of classes that are named an ID (something like ID12345). 基本上,我有一堆名为ID的类(类似于ID12345)。 These all inherit from the base builder class. 这些都继承自基础构建器类。 In my script, I need to instantiate an instance for each class (about 50) every time this app runs. 在我的脚本中,每次运行此应用程序时,我都需要为每个类(大约50个)实例化一个实例。 So, I'm trying to see if instead of doing something like this: 所以,我试图看看是否代替做这样的事情:

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

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

ID12345.run()
ID01234.run()

Can I do something like this (I know this doesn't work): 我可以做这样的事情(我知道这不起作用):

IDS = ["ID12345", "ID01234"]

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

That way, when I need to add a new one in the future, all I have to do is add the id to the IDS list, rather than peppering the new ID throughout the code. 这样,当我需要在将来添加一个新的时,我所要做的就是将ID添加到IDS列表中,而不是在整个代码中添加新的ID。

EDIT 编辑

Looks like there are some different opinions based on where the data is coming from. 根据数据的来源,看起来有一些不同的意见。 These IDs are entered in a file that no one else has access to. 这些ID输入到其他人无法访问的文件中。 I'm not reading the strings from the command line, and I'd like to be able to do as little alteration when adding a new ID in the future. 我不是从命令行读取字符串,并且我希望将来在添加新ID时能够进行少量更改。

If you wanted to avoid an eval(), you could just do: 如果你想避免使用eval(),你可以这样做:

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

Provided that the class is defined in (or imported into) your current scope. 前提是该类在当前范围内定义(或导入)。

Not sure this is what you want but it seems like a more Pythonic way to instantiate a bunch of classes listed in a string: 不确定这是你想要的,但它似乎是一种更加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>]

Never use eval() if you can help it. 如果可以帮助, 切勿使用eval() Python has so many better options (dispatch dictionary, getattr() , etc.) that you should never have to use the security hole known as eval() . Python有这么多更好的选择(调度字典, getattr()等等),你不应该使用被称为安全漏洞eval()

Simplest way is to just create a dict. 最简单的方法就是创建一个字典。

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

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

Then use it (your code example): 然后使用它(您的代码示例):

IDS = ["ID12345", "ID01234"]

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

I decided to put together the accepted answer and a comment on the accepted answer. 我决定将接受的答案对已接受的答案评论放在一起 I also added the overloaded __getitem__ so this looks more like a factory pattern. 我还添加了重载的__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 : I added in some error handling. 编辑 :我添加了一些错误处理。

Edited : Placed under permissive Creative Commons license . 编辑 :置于宽松的知识共享许可下 Enjoy. 请享用。

There's some stuff missing from your question, so I'm forced to guess at the omitted stuff. 你的问题中缺少一些东西,所以我不得不猜测遗漏的东西。 Feel free to edit your question to correct the omissions. 随意编辑您的问题以纠正遗漏。

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

This works very nicely. 这很好用。 It doesn't instantiate from a string -- in practice you don't often want this. 它不会从字符串中实例化 - 在实践中,您通常不需要这样做。 Sometimes, but rarely. 有时,但很少。 More commonly, you a list of class objects from which you want instance objects. 更常见的是,您需要从中获取实例对象的类对象列表。

If you actually are getting your class names from the command line, then you'd make the following small change. 如果您确实从命令行获取了类名,那么您将进行以下小改动。

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 ]

Everything else remains the same. 其他一切都是一样的。

[Also, if possible, try to start instance variable names with lowercase letters. [另外,如果可能,尝试使用小写字母启动实例变量名称。 Names which start with Uppercase Letters are usually class names.] 以大写字母开头的名称通常是类名。]

Edit 编辑

Removed eval . 删除了eval In spite of the fact that eval() is absolutely not a security hole. 尽管eval()绝对不是安全漏洞。 Eval (and exec and execfile ) are only a problem if someone specifically grants access to malicious users. 如果某人专门授予对恶意用户的访问权限,则Eval(以及execexecfile )只是一个问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM