Pythonic способ назначить экземпляр подкласса переменной, когда конкретная строка представлена ​​конструктору родительского класса

Я хочу иметь возможность создать экземпляр родительского класса X со строкой "Q" в качестве дополнительного аргумента.
Эта строка должна быть именем, являющимся идентификатором подкласса Q родительского класса X.< br> Я хочу, чтобы экземпляр родительского класса стал (или был заменен) экземпляром подкласса.

Я знаю, что это, вероятно, классическая проблема (ошибка?). Однако после некоторых поисков я не нашел подходящего решения.
Я сам придумал следующее решение:
я добавил словарь возможных идентификаторов в качестве ключей для их экземпляров базового класса в init-метод родительского класса.
Затем присвоил class-атрибут соответствующего подкласса текущим экземплярам class-attribute.
Мне потребовался аргумент метода init не должен быть значением по умолчанию, чтобы предотвратить бесконечное зацикливание.
Ниже приведен пример того, как код выглядит на практике;

class SpecialRule:
    """"""
    name="Special Rule"
    description="This is a Special Rule."
    def __init__(self, name=None):
        """"""
        print "SpecialInit"
        if name!=None:
            SPECIAL_RULES={
                "Fly" : FlyRule(),
                "Skirmish" : SkirmishRule()
                } #dictionary coupling names to SpecialRuleclasses
            self.__class__= SPECIAL_RULES[name].__class__

    def __str__(self):
        """"""
        return self.name

class FlyRule(SpecialRule):
    """"""
    name="Fly"
    description="Flies."
    def __init__(self):
        """"""
        print "FlyInit"+self.name
        SpecialRule.__init__(self)
    def addtocontainer(self, container):
        """this instance messes with the attributes of its containing class when added to some sort of list"""

class SkirmishRule(SpecialRule):
    """"""
    name="Skirmish"
    description="Skirmishes."
    def __init__(self):
        """"""
        SpecialRule.__init__(self)
    def addtocontainer(self, container):
        """this instance messes with the attributes of its containing class when added to some sort of list"""

test=SpecialRule("Fly")
print "evaluating resulting class"
print test.description
print test.__class__
</pre></code>

вывод:

> SpecialInit FlyInitFly SpecialInit evaluating resulting class Flies. main.FlyRule >

Есть ли более питоническое решение и есть ли предсказуемые проблемы с моим? (И я ошибаюсь, что хорошей практикой программирования является явный вызов .__init__(self) родительского класса в .__init__ подкласса?). Мое решение кажется немного... неправильным...

Краткий обзор.
Спасибо за быстрые ответы.

@ Решение Марка Толонена
Я изучал __new__-method, но когда я пытаюсь сделать A, B и C в примере Марка Толонена подклассами Z, я получаю сообщение об ошибке, что класс Z еще не определен. Также я не уверен, что создание экземпляра класса A обычным способом (с переменной=A() вне области Z) возможно, если только у вас уже не создан экземпляр подкласса и вы не вызываете класс как атрибут экземпляра подкласс Z... что не кажется очень простым. __new__ довольно интересен, поэтому я еще немного поиграю с ним, ваш пример легче понять, чем тот, что я получил из pythondocs.

@ Решение Грега Хьюгилла
Я попробовал решение staticmethod, и, похоже, оно работает нормально. Раньше я рассматривал возможность использования отдельной функции в качестве фабрики, но догадался, что будет сложно управлять большой программой со списком незакрепленных нитей кода конструктора в основном блоке, поэтому я очень рад интегрировать ее в класс.
Я немного поэкспериментировал, пытаясь превратить метод create в украшенный .__call__(), но он получился довольно грязным, поэтому я оставлю его на этом.


person Ben    schedule 01.08.2010    source источник


Ответы (2)


Я бы решил это, используя функцию, которая инкапсулирует выбор объекта:

class SpecialRule:
    """"""
    name="Special Rule"
    description="This is a Special Rule."
    @staticmethod
    def create(name=None):
        """"""
        print "SpecialCreate"
        if name!=None:
            SPECIAL_RULES={
                "Fly" : FlyRule,
                "Skirmish" : SkirmishRule
                } #dictionary coupling names to SpecialRuleclasses
            return SPECIAL_RULES[name]()
        else:
            return SpecialRule()

Я использовал декоратор @staticmethod, чтобы вы могли вызывать метод create(), еще не имея экземпляра объекта. Вы бы назвали это так:

SpecialRule.create("Fly")
person Greg Hewgill    schedule 01.08.2010
comment
Кстати, это известно как Factory Pattern. - person Seamus Campbell; 02.08.2010

Найдите метод __new__. Это правильный способ переопределить способ создания класса по сравнению с инициализацией.

Вот быстрый хак:

class Z(object):
    class A(object):
        def name(self):
            return "I'm A!"
    class B(object):
        def name(self):
            return "I'm B!"
    class C(object):
        def name(self):
            return "I'm C!"

    D = {'A':A,'B':B,'C':C}

    def __new__(cls,t):
        return cls.D[t]()
person Mark Tolonen    schedule 01.08.2010
comment
Обратите внимание, что (как прокомментировал Бен в своем редактировании) для того, чтобы A,B,C были подклассами Z, они должны быть объявлены вне объявления Z. И из-за нового __new__ у них, вероятно, должен быть свой собственный __new__, возвращающий ZsSuperClass.__new__(cls, *args) (насколько мне известно, вы не можете использовать super без существующего экземпляра, для которого вам нужно вызвать __new__...) - person Tobias Kienzler; 27.08.2013