可以使用字符串来实例化一个类吗?

发布于 2024-07-13 07:58:14 字数 833 浏览 4 评论 0原文

我在 Python 中使用 Builder 模式来分隔一堆不同的配置可能性。 基本上,我有一堆名为 ID... 的类(例如 ID12345)。 这些都继承自 Builder 基类。 在我的脚本中,每次运行此应用程序时,我都需要为每个类(大约 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 时能够进行尽可能少的更改。

I'm using a Builder pattern in Python to separate a bunch of different configuration possibilities. Basically, I have a bunch of classes that are named ID... (e.g. 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. 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.

EDIT:

Looks like there are different opinions based on where the data is coming from. These IDs are entered in a file that no one else has access to. 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

远昼 2024-07-20 07:58:14

如果您想避免使用 eval(),您可以这样做:

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

前提是该类是在当前作用域中定义(或导入)的。

If you wanted to avoid an eval(), you could just do:

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

Provided that the class is defined in (or imported into) your current scope.

旧人 2024-07-20 07:58:14

不确定这是否是您想要的,但这似乎是一种更 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>]

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:

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>]
埖埖迣鎅 2024-07-20 07:58:14

如果可以的话,永远不要使用eval()。 Python 有如此许多更好的选项(调度字典、getattr() 等),您永远不必使用名为 eval()< 的安全漏洞/代码>。

Never use eval() if you can help it. Python has so many better options (dispatch dictionary, getattr(), etc.) that you should never have to use the security hole known as eval().

拿命拼未来 2024-07-20 07:58:14

最简单的方法是创建一个字典。

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

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()
我爱人 2024-07-20 07:58:14

我决定将接受的答案对接受的答案发表评论。 我还添加了重载的 __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"))

编辑:我添加了一些错误处理。

已编辑:置于宽松的知识共享许可下。 享受。

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.

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.

失眠症患者 2024-07-20 07:58:14

你的问题遗漏了一些东西,所以我不得不猜测遗漏的东西。 请随意编辑您的问题以纠正遗漏。

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)才会出现问题。

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. In spite of the fact that eval() is absolutely not a security hole. Eval (and exec and execfile) are only a problem if someone specifically grants access to malicious users.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文