Page 1 of 1

Python - classes in multiple files; superconstructors

PostPosted: Thu Sep 06, 2012 8:38 pm
by BenChang
Question: how do I set up super classes in Python? How do I put classes in separate files?

In Python, to put classes in different files, use import:

animals.py
Code: Select all
class Cat:
    def __init__(self):
        print "i am a cat"


zoo.py
Code: Select all
import animals

c=animals.Cat()


you can use all the usual variants, like "from animals import *", "import animals.Cat", etc.

Creating subclasses is pretty straightforward - just put the superclass type in parens when declaring the subclass.

Code: Select all
class Cat:
    def __init__(self):
        print "i am a cat"
       
class Nyancat(Cat):
    def __init__(self):
        Cat.__init__(self)
        print "i am a nyancat"


Note that you must call the superclass constructor explicitly, which is different from how things work in some other OO languages. The syntax for that might seem a little weird and magical, but go with it.

You can also use what are called "new-style classes" in Python, which give you some additional OO features. In the example above, Cat doesn't inherit from anything, it just 'is'. If you define a class as inheriting from 'object', it becomes a new-style class. One of the things you get is the super() method, which is used to call methods in the superclass.

Code: Select all
class Dog(object):
    def __init__(self):
        print "i am a dog"
       
class Terrier(Dog):
    def __init__(self):
        super(Terrier,self).__init__()
        print "i am a terrier"


This example is for Python 2.x. Python 3.0 has tidier ways of using super(), but we're using Python 2.7 for everything in Game Development so I'll leave it at that.

super(class,instanceObject) takes instanceObject, considers it to be of type class, and determines the superclas in that context. It returns a proxy object that can access methods and properties of the parent class.

http://docs.python.org/library/functions.html#super

If you want to read up on more about the difference between the two ways of doing this, use some Google and stackoverflow. super() is particularly useful in resolving certain problems with multiple inheritance.

super() and multiple inheritance

PostPosted: Thu Sep 06, 2012 8:59 pm
by BenChang
I felt bad about leaving the end of that post so vague so here is a demonstration of the multiple inheritance issue. Enjoy.

When using the simple form of inheritance, you can end up with a superclass constructor getting called multiple times (same problem with any other method, constructors and destructors are just the most common places where this is an issue).

Code: Select all
class Critter:
    def __init__(self):
        print "I am a critter"

class Cat(Critter):
    def __init__(self):
        Critter.__init__(self)
        print "I am a cat"

class Robot(Critter):
    def __init__(self):
        Critter.__init__(self)
        print "I am a robot"

class Robocat (Cat,Robot):
    def __init__(self):
        Cat.__init__(self)
        Robot.__init__(self)
        print "I am robocat!"

r=Robocat()


output:
Code: Select all
I am a critter
I am a cat
I am a critter
I am a robot
I am robocat!


Here, Cats and Robots are both types of some base class of Critters that conceptually also includes squids, centipedes, puppies, Beholders, drone spacecraft, etc. Let's say we want to combine the furry cuteness of a cat with the destructive power of a robot and make a Robocat. We just inherit from both Cat and Robot in the class declaration. But notice what happens when the constructor gets called. Because of the naive way the chain of superclass constructors are called, the Critter constructor gets called twice. That'll be a problem if you're allocating or deleting memory, initializing anything that should only get initialized exactly once, doing something time consuming like loading a large file, registering the object with a manager class or networked server, etc.

Here's the same code but with new-style classes and using super():

Code: Select all
class Critter(object):
    def __init__(self):
        super(Critter,self).__init__()
        print "I am a critter"

class Cat(Critter):
    def __init__(self):
        super(Cat,self).__init__()
        print "I am a cat"

class Robot(Critter):
    def __init__(self):
        super(Robot,self).__init__()
        print "I am a robot"

class Robocat (Cat,Robot):
    def __init__(self):
        super(Robocat,self).__init__()
        print "I am robocat!"

r=Robocat()


output:

Code: Select all
I am a critter
I am a robot
I am a cat
I am robocat!


Now it only gets called once, which is the expected behavior.