f1nan f1nan - 1 year ago 69
Python Question

Understanding multiple inheritence and super based on some code from David Beazly

I watched a screencast from David Beazly in which he implemeneted type checking using multiple or more specifically diamond inheritence. I thought that his approach looked really cool but it also confused me and I simply can't figure out how it is working. Here is the code im talking about:

class Contract:
@classmethod
def check(cls, value):
pass


class Integer(Contract):
@classmethod
def check(cls, value):
assert isinstance(value, int), 'Expected int'
super().check(value)


class Positive(Contract):
@classmethod
def check(cls, value):
assert value > 0, 'Must be > 0'
super().check(value)


class PositiveInteger(Positive, Integer):
pass


And here it is in action:

>>> PositiveInteger.check(-3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in check
AssertionError: Must be > 0
>>> PositiveInteger.check(4.88)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in check
File "<stdin>", line 4, in check
AssertionError: Expected int


My Questions are:


  1. Why is the definition of a base class Contract with a method check needed to make this work?

  2. I have a basic understanding of what super does. I know it lets us avoid calling the base class explicitly and somehow deals with multiple inheritence. But what does it do in this example exactly?


Answer Source

Let's go through it line by line like a debugger.

PositiveInteger.check(x)

# Method resolution order:
# PositiveInteger, Positive, Integer, Contract (from Positive), Contract (from Integer)

# Look through MRO for .check() method. Found in Positive.

assert x > 0
super().check(value)

# super() checks for next .check() method in MRO. Found in Integer

assert isinstance(x, int)
super().check(value)

# super() checks for next .check() method in MRO. Found in Contract

pass

To easily find the method resolution order, use inspect.getmro().

If you had explicitly used the base class, after Positive, the base class is Contract, so Integer would never be called.

You need to define .check() in Contract as when you call the last super(), if Contract didn't have the .check() method, it would have raised an AttributeError, as super() wouldn't have been able to find it.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download