2

I have some functionality wrapped up in a Python class (classa). classa inherits from another class supera.

I want exactly the same functionality as classa except that I want to inherit from superb.

I could just copy the class classa to a new class classb and then change the superclass for classb but obviously this very tacky, a maintenance headache and and I'm quite sure there's much better way - can anyone tell me what it is ?


EDIT: Thanks for answers so far. I should have said initially the my classa invokes super in its methods in order to invoke supera methods. It seems that this has some significance when looking at mixins as an option

glaucon
  • 8,112
  • 9
  • 41
  • 63
  • 1
    It'd be helpful if you could show your super and sub classes. Mixins may be an option, or dynamically constructing your types might be better, all depending on what's in the classes themselves. – Josh Smeaton Apr 16 '13 at 01:38
  • Similar question: http://stackoverflow.com/questions/9539052/python-dynamically-changing-base-classes-at-runtime-how-to – rmmh Apr 16 '13 at 01:40
  • @JoshSmeaton OK I'll try to do that. Can't show the actual code 'cos it's not mine but will try to add the bare bones of it as an edit to the question later on today. I will also edit the question to make clear that the subclass needs to use 'super' to invoke methods in the superclass (which may be significant wrt mixins) – glaucon Apr 16 '13 at 02:10
  • @rmmh thanks for pointing that out. I'm going to try out and see ... I suppose it does verge a little too close to 'magic' perhaps. However I will try it - thanks again. – glaucon Apr 16 '13 at 02:11
  • 1
    This can probably be done with a factory method and the `type` function, but attempting to do so is usually a function of some bad design decision that's been made. If you can show your classes, we may be able to come up with an alternative design for you, rather than hacking the type hierarchy. – Josh Smeaton Apr 16 '13 at 02:19
  • It doesn't seem that inheritance is a good choice here. Why not replace it with composition? You can have `supera` (or `superb`) object created by and contained within your `classa` object. You can use `getattr` to catch all the calls to methods not defined in `classa` (except the `__***__` methods) and forward them to the `supera` / `superb` contained object. – max Apr 16 '13 at 03:20

1 Answers1

4

This can be done with Python's multiple inheritance if none of the methods need to invoke super().

class Dog(object):
    name = "Spot"

class Cat(object):
    name = "Whiskers"

class SpeakingAnimalMixin(object):
    def speak(self):
        print "My name is", self.name, "and I can speak!"

class SpeakingDog(SpeakingAnimalMixin, Dog):
    pass

class SpeakingCat(SpeakingAnimalMixin, Cat):
    pass

SpeakingDog().speak()

My name is Spot and I can speak!


If you do need to invoke super() from a method then you need to create the class dynamically. This works fine, but the generated class's name will be less helpful and IDEs and other static analysis tools may be less useful.

You can create the class using a function, passing the superclass as an argument:

def make_speaking_animal_class(SpeechlessAnimal):
    class SpeakingAnimal(SpeechlessAnimal):
        def get_name(self):
            return "Speaking " + super(SpeakingAnimal, self).get_name()

        def speak(self):
            print "My name is", self.get_name()

    return SpeakingAnimal

class Dog(object):
    def get_name(self):
        return "Spot"

class Cat(object):
    def get_name(self):
        return "Whiskers"

SpeakingDog = make_speaking_animal_class(Dog)
SpeakingCat = make_speaking_animal_class(Cat)

SpeakingCat().speak()

My name is Speaking Whiskers

However as mentioned, the class's __name__ attribute may not be what you expect.

print SpeakingDog
print SpeakingDog()
<class '__main__.SpeakingAnimal'>
<__main__.SpeakingAnimal object at 0x1004a3b50>

You can fix this by assigning them unique __name__ attributes yourself:

SpeakingDog.__name__ = 'SpeakingDog'
print SpeakingDog
<class '__main__.SpeakingDog'>

(Credit to Andrew Jaffe for suggesting this in an answer, but he deleted it.)

There's another way to create a class dynamically, but I discourage you from using it unless you need to; it's even less clear. The type function has a second use, apart from its main one of determining the class of an object: it can be used to dynamically create a new class.

When used this way, the type function takes three parameters:

  • name, the __name__ the new class will have.
  • bases, a tuple of of base classes that the new class will inherit from.
  • dict, a dictionary containing the methods and attributes the new class will have.

You could use it like this:

def make_speaking_animal_class(SpeechlessAnimal, name):
    def get_name(self):
        return "Speaking " + super(SpeakingAnimal, self).get_name()

    def speak(self):
        print "My name is", self.get_name()

    bases = (SpeechlessAnimal,)

    # We need to define SpeakingAnimal in a variable so that get_name can refer
    # to it for the super() call, otherwise we could just return it directly.
    SpeakingAnimal = type(name, bases, {
        'get_name': get_name,
        'speak': speak
    })

    return SpeakingAnimal

class Dog(object):
    def get_name(self):
        return "Spot"

class Cat(object):
    def get_name(self):
        return "Whiskers"

SpeakingDog = make_speaking_animal_class(Dog, 'SpeakingDog')
SpeakingCat = make_speaking_animal_class(Cat, 'SpeakingCat')

SpeakingDog().speak()
SpeakingCat().speak()

My name is Speaking Spot
My name is Speaking Whiskers

Community
  • 1
  • 1
Jeremy
  • 1
  • 85
  • 340
  • 366
  • Thank you for your answer which was interesting and informative independent of this question ... however unfortunately the subclass does need to invoke 'super' from within the subclass methods so I think this approach is not my answer. Thanks again. – glaucon Apr 16 '13 at 02:07
  • @glaucon I've expanded my answer to address that. :) – Jeremy Apr 16 '13 at 02:22