Подтвердить что ты не робот

Можете ли вы использовать строку для создания экземпляра класса?

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

ИЗМЕНИТЬ

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

4b9b3361

Ответ 1

Не уверен, что это то, что вы хотите, но это похоже на более 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

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

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

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

Ответ 3

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

Ответ 4

Самый простой способ - просто создать dict.

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

Ответ 5

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

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 ]

Все остальное остается прежним.

[Также, если возможно, попробуйте запустить имена переменных экземпляра строчными буквами. Имена, которые начинаются с верхних букв, обычно являются именами классов.]

Edit

Удалено eval. Несмотря на то, что eval() является абсолютно не дырой безопасности. Eval (и exec и execfile) - это только проблема, если кто-то конкретно предоставляет доступ к вредоносным пользователям.

Ответ 6

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