194

What does mro() do?


Example from django.utils.functional:

for t in type(res).mro():  # <----- this
    if t in self.__dispatch:
        return self.__dispatch[t][funcname](res, *args, **kw)
1

5 Answers 5

281

Follow along...:

>>> class A(object): pass
... 
>>> A.__mro__
(<class '__main__.A'>, <type 'object'>)
>>> class B(A): pass
... 
>>> B.__mro__
(<class '__main__.B'>, <class '__main__.A'>, <type 'object'>)
>>> class C(A): pass
... 
>>> C.__mro__
(<class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
>>> 

As long as we have single inheritance, __mro__ is just the tuple of: the class, its base, its base's base, and so on up to object (only works for new-style classes of course).

Now, with multiple inheritance...:

>>> class D(B, C): pass
... 
>>> D.__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)

...you also get the assurance that, in __mro__, no class is duplicated, and no class comes after its ancestors, save that classes that first enter at the same level of multiple inheritance (like B and C in this example) are in the __mro__ left to right.

Every attribute you get on a class's instance, not just methods, is conceptually looked up along the __mro__, so, if more than one class among the ancestors defines that name, this tells you where the attribute will be found -- in the first class in the __mro__ that defines that name.

Sign up to request clarification or add additional context in comments.

4 Comments

hi,alex,is there any difference between D.__mro__ and D.mro().
mro can be customized by a metaclass, is called once at class initialization, and the result is stored in __mro__ -- see docs.python.org/library/… .
Why is it called method resolution order instead of attribute resolution order?
Just like @Alex Martelli said and the content of python-history.blogspot.com/2010/06/…, the mro attribute should be add when the new class are used, as only when Python 2.2 MRO and Python 2.3 MRO(C3) are used.
104

mro() stands for Method Resolution Order. It returns a list of types the class is derived from, in the order they are searched for methods.

mro() and __mro__ work only on new style classes. In Python 3, they work without any issues. In Python 2, however, those classes need to inherit from object.

Comments

23

This would perhaps show the order of resolution.

class A(object):
    def dothis(self):
        print('I am from A class')

class B(A):
    pass

class C(object):
    def dothis(self):
        print('I am from C class')

class D(B, C):
    pass

d_instance = D()
d_instance.dothis()
print(D.mro())

The output would be:

I am from A class
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.C'>, <class 'object'>]

The rule is depth-first, which in this case would mean D, B, A, C.

Python normally uses a depth-first order when searching inheriting classes, but when two classes inherit from the same class, Python removes the first mention of that class from the MRO.

3 Comments

So as you can see the order is D, B, A, C
What do you mean by "Python removes the first mention of that class from mro."?
@RuthvikVaila: I think that part is just poorly stated. In this blog post about the history of Python, Guido van Rossum remarks (about the MRO scheme introduced in Python 2.2) "If any class was duplicated in this search, all but the last occurrence would be deleted from the MRO list" (emphasis mine). This implies that it could remove more than just the first "mention" of the class.
3

Order of resolution will be different in diamond inheritance.

class A(object):
    def dothis(self):
        print('I am from A class')


class B1(A):
    def dothis(self):
        print('I am from B1 class')
    # pass


class B2(object):
    def dothis(self):
        print('I am from B2 class')
    # pass


class B3(A):
    def dothis(self):
        print('I am from B3 class')


# Diamond inheritance
class D1(B1, B3):
    pass


class D2(B1, B2):
    pass


d1_instance = D1()
d1_instance.dothis()
# I am from B1 class
print(D1.__mro__)
# (<class '__main__.D1'>, <class '__main__.B1'>, <class '__main__.B3'>, <class '__main__.A'>, <class 'object'>)


d2_instance = D2()
d2_instance.dothis()
# I am from B1 class
print(D2.__mro__)
# (<class '__main__.D2'>, <class '__main__.B1'>, <class '__main__.A'>, <class '__main__.B2'>, <class 'object'>)

3 Comments

but why the resolution is different ?
In the multiple inheritance scenario, any specified attribute is searched first in the current class. If not found, the search continues into parent classes in depth-first, left-right fashion without searching same class twice.
sorry but i don't understand why in first case it's go to class B3 but in second case it's go to class A after class B1
0

For @stryker 's example, the C3 algorithm is:

L[D(B,C)] = D + merge(BAo, Co, BC)
          = D + B + merge(Ao, Co, C)
          = DB + A + merge(o, Co, C)
          = DBA + merge(Co, C)
          = DBACo

See The Python 2.3 Method Resolution Order | Python.org

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.