181

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)
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
zjm1126
  • 63,397
  • 81
  • 173
  • 221

5 Answers5

267

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.

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 10
    hi,alex,is there any difference between D.__mro__ and D.mro(). – zjm1126 Jan 06 '10 at 03:55
  • 31
    `mro` can be customized by a metaclass, is called once at class initialization, and the result is stored in `__mro__` -- see http://docs.python.org/library/stdtypes.html?highlight=mro#class.__mro__ . – Alex Martelli Jan 06 '10 at 04:24
  • 28
    Why is it called **method resolution order** instead of **attribute resolution order**? – Bentley4 Mar 24 '13 at 13:07
  • 1
    Just like @Alex Martelli said and the content of http://python-history.blogspot.com/2010/06/method-resolution-order.html, 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. – andy Aug 26 '14 at 05:54
100

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.

Pranithan T.
  • 323
  • 1
  • 6
  • 14
Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
19

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.

Stryker
  • 5,732
  • 1
  • 57
  • 70
  • 1
    So as you can see the order is D, B, A, C – Stryker Mar 21 '16 at 21:57
  • 2
    What do you mean by "Python removes the first mention of that class from mro."? – Ruthvik Vaila Jul 03 '17 at 23:01
  • 4
    @RuthvikVaila: I think that part is just poorly stated. In this [blog post](http://python-history.blogspot.com/2010/06/method-resolution-order.html) 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. – martineau Jul 30 '17 at 16:30
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'>)
Girish Gupta
  • 1,241
  • 13
  • 27
  • but why the resolution is different ? – Vadim Aug 20 '18 at 10:18
  • 1
    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. – Girish Gupta Aug 28 '18 at 18:37
  • 1
    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` – Vadim Nov 21 '18 at 19:20
-1

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

weaming
  • 5,605
  • 1
  • 23
  • 15