10

I have created a Python class called Liger, which extends a class called Lion and a class called Tiger. The class Liger inherits the method speak() from both Lion and Tiger, but the syntax is valid nonetheless - no error message is printed, but instead, Tiger's implementation of the speak() method is inherited by Liger. Is it possible to detect method name collisions like this one in Python, so that an error message is printed when method names conflict in this way?

'''
Conflicting method names in python
'''


class Tiger():
    @staticmethod
    def speak():
        print "Rawr!";

class Lion():
    @staticmethod
    def speak():
        print "Roar!";

class Liger(Tiger, Lion):
    pass
'''both superclasses define a speak() method, and I need a way to detect this type of conflict.'''    

Liger.speak(); ''' this prints "Rawr" instead of printing an error message. '''
'''Is there any way to detect method name collisions like this one?'''

The code can be tested and debugged online here: http://ideone.com/xXOoVq

4
  • 2
    Finesse the problem by not using multiple inheritance? Commented Mar 10, 2013 at 22:39
  • 2
    @hughdbrown This is a question about multiple inheritance in Python, and I consider multiple inheritance to be very useful in some situations (for example, using classes to model biological relationships between species, where interbreeding between different subspecies occurs, where all the species have a single common ancestor). I'd rather find a solution to this problem than avoid solving the problem entirely. Commented Mar 10, 2013 at 22:45
  • If I were creating some kind of epic fantasy game, I might have a scenario where the class Dragon inherits from the class Lizard and Reptile, where both Lizard and Reptile inherit from the class Amphibian, where the class Amphibian inherits from the class Fish. In this (very amusing) scenario, multiple inheritance would be absolutely indispensable. Commented Mar 10, 2013 at 22:57
  • 2
    Professional/modern game development uses entity-component systems rather than class-based inheritance (certainly not multiple inheritance) systems precisely because of problems like this. Yes, multiple inheritance has its uses, but modeling game entities ain't one of 'em! Commented Mar 11, 2013 at 0:31

2 Answers 2

4

You can detect this kind of conflicts using a metaclass:

class ConflictCheck(type):
    def __new__(meta, name, bases, dct):
        # determine attributes per base class, except for magic ones
        attrs_per_base = [set(a for a in dir(b) if not a.startswith("__"))
                          for b in bases]
        if len(set.union(*attrs_per_base)) < sum(map(len, attrs_per_base)):
            raise ValueError("attribute conflict")
        return super(ConflictCheck, meta).__new__(meta, name, bases, dct)

class Liger(Lion, Tiger):
    __metaclass__ = ConflictCheck  # will raise an error at definition time

This is a very crude first version with poor error messages, that will actually raise an error whenever a method is overridden above Liger in the inheritance tree, but it should be enough to get you started.

Sign up to request clarification or add additional context in comments.

8 Comments

Won't this also raise an error if you override any methods, anywhere in the inheritance tree?
@Blckknght: yes, or at least anywhere above Liger in the inheritance tree, as I indicated below the code. If you know how to get the direct superclasses, please tell me.
Here it is on ideone, so that it can be tested and debugged online: ideone.com/bZMSwt
@larsmans: I think you're actually closer than I initially thought. __bases__ does give the immediate superclasses, so not all overrides will break it, like I had initially thought. However, dir gives values that were inherited from further up the tree, so if the base classes have a common ancestor with a non-special method, you'll get a conflict on it.
@Blckknght: would b.__dict__ be better? (I don't define metaclasses on a daily basis...)
|
4

I'm not sure if inheritance is the right mechanism for you to use to solve your problem. Inheritance is often said to define an an "IS-A" relationship between two classes. That is, if A inherits from B, every instance of B is an instance of A also.

If you're using multiple inheritance, the class ends up being multiple kinds of object at once. In your example, a Liger instance, is both a Tiger and a Lion at the same time.

You probably don't want to be using class inheritance to model species inheritance. Despite the name, they don't really mean the same thing. For instance, housecats are probably descended from something quite tigerish, but it would not be correct to say that a housecat IS-A tiger.

The fact that you're using static methods on your classes makes me think that perhaps you don't want to be using classes at all, but rather instances of a more generic Animal class:

class Animal(object):
    def __init__(self, sound):
        self.sound = sound

    def speak(self):
        print self.sound

lion = Animal("Roar!")

There's no mechanism for inheritance there, but it would be possible to add a method that mutates one kind of Animal into another one in some way.

Now, if you are really set on using classes and multiple inheritance, there are two decent approaches (using regular instance methods, rather than static methods):

The first is to do "collaborative multiple inheritance", and have each of your methods call the same method on the next class in the MRO. Usually you need to inherit from a common base class to make this work (otherwise the last call to super will get object, which won't have the method defined):

class Animal(object):
    def speak(self):
        pass

class Lion(Animal):
    def speak(self):
        print("Roar!")
        super(Lion, self).speak()

class Tiger(Animal):
    def speak(self):
        print("Rawr!")
        super(Tiger, self).speak()

class Liger(Lion, Tiger):
    pass

In this situation, a Liger instance's speak method will print both Roar then Rawr, which makes sense since it's both a lion and a tiger at once. Be careful with this style of coding though, as it can be tricky to get the collaboration to work right. For instance, you can't change a method signature and expect it to work correctly in a collaborative multiple inheritance situation (unless you use only keyword arguments, and pass along any unrecognized ones in **kwargs).

The second solution is to give Liger it's own speak implementation that explicitly chooses what to do (which may be to call one of its base classes). This is required in some other languages, like C++. If the inheritance you've requested isn't doing what you want, it's probably the most straightforward way to fix it:

class Liger(Lion, Tiger):
    def speak(self):
        Tiger.speak(self)

3 Comments

How will each of these solutions make it easier to detect method conflicts in the superclasses of a single class? (I suppose the first solution might be more useful than the second, since both of the superclasses' methods would be combined into one, making method conflicts more noticeable.)
Also, in my original example, all of the classes' methods were static methods. In this answer, none of the methods are static methods.
@AndersonGreen: In brief, I think that detecting and raising an error on method conflicts is solving the wrong problem. If multiple inheritance is the right solution at all, you want to be able to have conflicting methods (with overrides or super calls to get the right results from them). As for using static methods, that seemed like a very strange choice (the species "Tiger" doesn't growl, individual tigers do). I'll edit my answer to be more explicit about what I'm answering.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.