Posts Tagged ‘inheritance’

Articles

Python’s new classes vs old classes

In Python on November 18, 2010 by Matt Giuca Tagged: ,

I just got my head around the “method resolution order” (MRO) rules for new classes in Python. If you aren’t aware, in Python 2.2 (now nine years old, from 2001) they introduced so-called “new-style classes”. Basically, to create a new-style class, you inherit from the built-in class object or any other new-style class. If you don’t inherit from anything, you create an old-style class. (And in Python 3 onwards, there are no more old-style classes, so you no longer need to write (object) on every class.)

I’ve known for a while that they’re “good” and I should use them. Aside from the immediate benefits of being able to inherit from built-in types and them being unified with the type system (type(myobject) == MyClass and type(MyClass) == type, rather than type(myobject) == instance and type(MyClass) == classobj), I knew there was something “fixed” about the method resolution order — the set of rules for deciding which version of an overridden method to call in a complex multiple-inheritance hierarchy.

Now having read  The Python 2.3 Method Resolution Order, I understand it, but that document is a little wordy, so here is the simplest example I can think of. I’ll build the classic diamond problem, out of old-style classes:

class D:       # Note: Old-style
    def f(self): return "D.f()"
class B(D): pass
class C(D):
    def f(self): return "C.f()"
class A(B, C): pass

>>> a = A()
>>> a.f()
'D.f()'

The f method of B returns “D.f()” (due to inheriting from D and not overriding). The f method of C returns “C.f()”. Obviously since A inherits both B and C, there is some contention about which to use. Under the old rules (depth first left to right), it chooses the version in B, even though C is closer to A and in some sense “more authoritative”. It’s certainly a tricky question: it seems like it should have chosen the version in C, because after all, C has explicitly overridden the version in D and B hasn’t. But on the other hand, why should B behave differently depending on whether it inherited a definition or supplied its own?

In any event, the new-style rules favour the “most authoritative” version:

class D(object):       # Note: New-style
    def f(self): return "D.f()"
class B(D): pass
class C(D):
    def f(self): return "C.f()"
class A(B, C): pass

>>> a = A()
>>> a.f()
'C.f()'

Because C overrides D, it “wins”. The actual logic here is to go up depth-first but stop when you find a class which is subclassed later on. For example, when looking for the definition of f to use, it tries to go up A – B – D, but stops when it realises that D is being subclassed by a class which we haven’t considered yet (C). So it considers C first before looking at D, and finds a “stronger” definition there.

Most importantly, this new rule ensures monotonicity, and that is explained in the article.

[Edit: I’m pretty sure when the article talks about “Python 2.2 classes”, it does not refer to “old-style classes”, but rather the initial implementation of new-style classes in Python 2.2, which was flawed, and fixed in Python 2.3. So it is no longer possible to try out the “flawed” behaviour mentioned there without going back and installing version 2.2.]

Advertisements

Articles

Still totally undecided about implementation inheritance

In Language design,Object-orientation on September 14, 2010 by Matt Giuca Tagged: , ,

Everyone keeps saying it’s a bad thing. They cite the fragile base class problem and other various issues which I don’t really have a problem with. (The FBC problem as far as I can tell almost always is caused when someone violates the Liskov substitution principle.)

I recently went to a Go talk. Go is another in a string of languages which extol the virtues of interface inheritance and not implementation inheritance. But in most of the OO code I write (including the code I encourage and expect my students to write), I use class hierarchies with a lot of data and code re-use in the child classes, I think to great effect. Am I mistaken?

To this end I shall undergo an experiment: Having never used Go, I will rewrite the Java project I gave my students in Go, making extensive use of interface inheritance and see if there’s a natural “Go-ism” for achieving code and data re-use where my existing code uses implementation inheritance — I assume using ordinary composition and functions.