Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 407
Ben Avatar asked Aug 01 '10 20:08

Ben


People also ask

How do you use an instance variable inside a class method in Python?

There are two ways to access the instance variable of class:Within the class by using self and object reference. Using getattr() method.

How do you add an instance variable in Python?

We can access the instance variable using the object and dot ( . ) operator. In Python, to work with an instance variable and method, we use the self keyword. We use the self keyword as the first parameter to a method.

Is there a way to check if a class is a subclass of another class in Python?

Python issubclass() is built-in function used to check if a class is a subclass of another class or not. This function returns True if the given class is the subclass of given class else it returns False .

How do you call a super class constructor when initializing a subclass instance?

Use super() to invoke the super constructorCall __init__() on super() inside the constructor of the subclass to invoke the constructor of the superclass.


1 Answers

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")
like image 106
Greg Hewgill Avatar answered Sep 24 '22 03:09

Greg Hewgill