1

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?

f1nan
  • 156
  • 10
  • Related, if not duplicate: [How does Python's super() work with multiple inheritance?](https://stackoverflow.com/questions/3277367/how-does-pythons-super-work-with-multiple-inheritance) – Nick T Jul 26 '17 at 21:13
  • @NickT: I am aware of this thread and I did read it but it didn't help me understanding this problem – f1nan Jul 26 '17 at 21:17
  • I think the idea is that somewhere in the MRO there should/must be a method so you put it in the base class - even if it doesn't do anything or just raises a NotImplemented exception. – wwii Jul 26 '17 at 22:03
  • 1
    Take a look at the practical Advice section of [Python's super() considered super!](https://rhettinger.wordpress.com/2011/05/26/super-considered-super/) – wwii Jul 26 '17 at 22:11
  • @wwii: I think I did express myself unclear. Could you have a look at my comment at Artyer's answer? – f1nan Jul 26 '17 at 22:16
  • You have asked two questions in this question - one deals with a different solution which you haven't provided. You also seem to have modified your question with a comment to an answer. Maybe you should regroup, think about your problem and ask specific questions (one at a time) that help you get to an understanding. – wwii Jul 26 '17 at 22:25
  • @wwii: It wasn't my intetnion to modify my original question. This is probably due to my poor english. But thanks for your advice. – f1nan Jul 26 '17 at 22:46

1 Answers1

1

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.

Artyer
  • 31,034
  • 3
  • 47
  • 75