开发者

Pythonic way to assign an instance of a subclass to a variable when a specific string is presented to the constructor of the parent class

开发者 https://www.devze.com 2023-01-09 14:50 出处:网络
I want to be able to create an instance of a parent class X, with a string \"Q\" as an extra argument.

I want to be able to create an instance of a parent class X, with a string "Q" as an extra argument.

This string is to be a name being an identifier for a subclass Q of the parent class X.

I want the instance of the parent class to become (or be replaced with) an instance of the subclass.

I am aware that this is probably a classic problem (error?). After some searching I haven't found a suitable solution though.

I came up with the following solution myself;

I added a dictionary of possible identifiers as keys for their baseclass-instances to the init-method of the parent class.

Then assigned the class-attribute of the corresponding subclass to the current instances class-attribute.

I required the argument of the init-method not to be the default value to prevent infinite looping.

Following is an example of what the code looks like in practice;

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>

output:

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

Is there a more pythonic solution and are there foresee-able problems with mine? (And am I mistaken that its a good programming practice to explicitly call the .__init__(self) of the parent class in .__init__ of the subclass?). My solution feels a bit ... wrong ...

Quick recap so far;

Thanks for the quick answers

@ Mark Tolonen's solution

I've been looking into the __new__-method, but when I try to make A, B and C in Mark Tolonen's example subclasses of Z, I get the error that class Z isn't defined yet. Also I'm not sure if instantiating class A the normal way ( with variable=A() outside of Z's scope ) is possible, unless you already have an instance of a subclass made and call the class as an attribute of an instance of a subclass of Z ... which doesn't seem very straightforward. __new__ is quite interesting so I'll fool around with it a bit more, your example is easier to grasp than what I got from the pythondocs.

@ Greg Hewgill's solution

I tried the staticmethod-solution and it seems to work fine. I looked into using a seperate function as a factory before but I guessed it would get hard to manage a large program with a list of loose strands of constructor code in the main block, so I'm very happy to integrate it in the class.

I did experiment a bit seeing if I could turn the create-method into a decorated .__call__() but it got quite messy so I'll leave it at that.


I would solve this by using a function that encapsulates the choice of object:

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

I have used the @staticmethod decorator to allow you to call the create() method without already having an instance of the object. You would call this like:

SpecialRule.create("Fly")


Look up the __new__ method. It is the correct way to override how a class is created vs. initialized.

Here's a quick hack:

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

精彩评论

暂无评论...
验证码 换一张
取 消