Можете ли вы использовать строку для создания экземпляра класса? - PullRequest
34 голосов
/ 16 февраля 2009

Я использую шаблон компоновщика для разделения множества различных возможностей конфигурации. По сути, у меня есть несколько классов, которые называются 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()

Таким образом, когда мне нужно добавить новый в будущем, все, что мне нужно сделать, это добавить идентификатор в список IDS, а не добавлять новый идентификатор в код.

EDIT

Похоже, что существуют разные мнения относительно того, откуда поступают данные. Эти идентификаторы заносятся в файл, к которому никто другой не имеет доступа. Я не читаю строки из командной строки, и я хотел бы иметь возможность вносить небольшие изменения при добавлении нового идентификатора в будущем.

Ответы [ 6 ]

55 голосов
/ 16 февраля 2009

Если вы хотите избежать eval (), вы можете просто сделать:

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

При условии, что класс определен в вашей текущей области (или импортирован в нее).

17 голосов
/ 17 февраля 2009

Не уверен, что это то, что вам нужно, но похоже на более 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>]
9 голосов
/ 16 февраля 2009

Никогда используйте eval(), если можете помочь. В Python есть , поэтому намного лучше (словарь рассылки, getattr() и т. Д.), Поэтому вам никогда не придется использовать дыру в безопасности, известную как eval().

5 голосов
/ 16 февраля 2009

Самый простой способ - просто создать диктовку.

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()
1 голос
/ 09 декабря 2018

Я решил собрать принятый ответ и комментарий к принятому ответу. Я также добавил перегруженный __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"))

Отредактировано : я добавил некоторую обработку ошибок.

Отредактировано : Помещено под разрешительную лицензию Creative Commons . Наслаждайтесь.

0 голосов
/ 16 февраля 2009

В вашем вопросе чего-то не хватает, поэтому я вынужден угадать пропущенные вещи. Не стесняйтесь редактировать свой вопрос, чтобы исправить упущения.

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) являются проблемой, только если кто-то специально предоставляет доступ злоумышленникам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...