A simplified summary of Python's method resolution order
Crudely summarized, method resolution order is how Python decides where to look for a method (or any other attribute) on a class that inherits from several classes. Python actually has two; a simple one for old style classes and a rather complicated one for new style classes.
(Technically, method resolution order applies even for single inheritance classes, it's just that they have a very boring one.)
With the old style class:
class Foo(A, B, C): pass
Python will look up
Foo.foo by looking for
foo on A and all of its
ancestors, then B and all its ancestors, and finally C and all its
ancestors; that is, the method resolution order is left to right, depth
This order is nice and simple but blows up if A and B have a common
ancestor with behavior that B wants to override, which is why new style
classes need a different scheme. (All new style classes are ultimately
object, which defines various default actions for things
like getting and setting attributes.)
The complete description of the method resolution order for new style classes is somewhat complicated. For simple class structures, it can be summarized as left to right and depth first but common ancestor classes are only checked after all of their children have been checked. Thus, with new style classes:
class A1(object): pass class A2(object): pass class A3(object): pass class B2(A2): pass class B3(A3): pass class C3(B3): pass class Foo(C3, A1, B2): pass
The method resolution order for
Foo.foo is Foo, C3, B3, A3, A1, B2,
A2, and then
object; as the common ancestor,
object is checked
only after all of its children have been. Note that the MRO can vary
drastically between a parent and a child class; C3's MRO is just C3, B3,
In case of doubt or curiosity you can find the MRO of any new style
class in its
__mro__ attribute. Normally you don't need to care
about its value and should use the
super() builtin if you need to find
the next class up in the context of a particular object or class.
(This is the kind of entry I write partly to make sure I have all this straight in my own head.)
My zeroth law of compromised machines
If you can't find anything wrong, you haven't looked carefully enough.
The immediate corollary is also important:
If you can't find anything, the intruders are still there.
The leading cause for not finding anything wrong on a machine you know is compromised is that you haven't detected the rootkit that is hiding things from you.
A note about the ordering of mixin classes
In Python, when you have a class that inherits from both a primary class and some mixin classes (for example, if you're using the SocketServer stuff), it's conventional to declare your class's inheritance list with the primary class first:
class Real(primary, mixin1, mixin2): ....
However, an important safety tip: if your mixin class overrides methods of the primary class, it has to be first. Failure to observe this safety tip can cause head-scratching bugs followed by head-smacking embarrassment.
(Since I was mixing stuff in to standard types like
str, I spent a certain amount of time wondering if the interpreter
had special direct magic for them that meant I couldn't hijack and
augment their standard behavior. I felt somewhat foolish when there
turned out to be a much simpler explanation.)