Wandering Thoughts archives

2007-02-22

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 first.

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 rooted at 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, A3, and object.

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.)

python/MethodResolutionOrder written at 23:22:06; Add Comment

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.

sysadmin/CompromisedMachinesLaw written at 15:22:25; Add Comment

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 list and tuple and 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.)

python/MixinOrderingNote written at 11:13:10; Add Comment


Page tools: See As Normal.
Search:
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.