Que fait «mro ()»?

Réponses:

211

Suivre...:

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

Tant que nous avons un héritage unique, __mro__est juste le tuple de: la classe, sa base, la base de sa base, et ainsi de suite jusqu'àobject (ne fonctionne que pour les classes de nouveau style bien sûr).

Maintenant, avec l' héritage multiple ...:

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

... vous avez également l'assurance que, dans __mro__ qu'aucune classe n'est dupliquée et qu'aucune classe ne vient après ses ancêtres, sauf que les classes qui entrent en premier au même niveau d'héritage multiple (comme B et C dans cet exemple) sont dans le __mro__de gauche à droite.

Chaque attribut que vous obtenez sur l'instance d'une classe, pas seulement les méthodes, est recherché conceptuellement le long du __mro__, donc, si plus d'une classe parmi les ancêtres définit ce nom, cela vous indique où l'attribut sera trouvé - dans la première classe de le __mro__qui définit ce nom.

Alex Martelli
la source
9
salut, Alex, y a-t-il une différence entre D .__ mro__ et D.mro ().
zjm1126
26
mropeut être personnalisé par une métaclasse, est appelé une fois à l'initialisation de la classe et le résultat est stocké dans __mro__- voir docs.python.org/library/… .
Alex Martelli
23
Pourquoi est-ce appelé ordre de résolution de méthode au lieu d' ordre de résolution d'attribut ?
Bentley4
1
Tout comme @Alex Martelli l'a dit et le contenu de python-history.blogspot.com/2010/06/… , l' attribut mro doit être ajouté lorsque la nouvelle classe est utilisée, comme uniquement lorsque Python 2.2 MRO et Python 2.3 MRO (C3) sont utilisés.
andy
82

mro()signifie Ordre de résolution des méthodes. Il renvoie une liste de types dont la classe est dérivée, dans l'ordre dans lequel les méthodes sont recherchées.

mro () ou __mro__ ne fonctionne que sur les nouvelles classes de style. En python 3, ils fonctionnent sans aucun problème. Mais en python 2, ces classes doivent hériter de object.

Ned Batchelder
la source
10

Cela montrerait peut-être l'ordre de résolution.

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

et la réponse serait

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

La règle est la profondeur d'abord, ce qui dans ce cas signifierait D, B, A, C.

Python utilise normalement un ordre de profondeur d'abord lors de la recherche de classes héritées, mais lorsque deux classes héritent de la même classe, Python supprime la première mention de cette classe de mro.

Stryker
la source
1
Donc, comme vous pouvez le voir, l'ordre est D, B, A, C
Stryker
1
Qu'entendez-vous par "Python supprime la première mention de cette classe de mro."?
Ruthvik Vaila
2
@RuthvikVaila: Je pense que cette partie est simplement mal énoncée. Dans ce billet de blog sur l'histoire de Python, Guido van Rossum remarque (à propos du schéma MRO introduit dans Python 2.2) "Si une classe était dupliquée dans cette recherche, toutes les occurrences sauf la dernière seraient supprimées de la liste MRO" (c'est moi qui souligne ). Cela implique qu'il pourrait supprimer plus que la première "mention" de la classe.
martineau
1

L'ordre de résolution sera différent dans l'héritage de diamants.

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
la source
mais pourquoi la résolution est-elle différente?
Vadim
Dans le scénario d'héritage multiple, tout attribut spécifié est recherché en premier dans la classe actuelle. S'il n'est pas trouvé, la recherche se poursuit dans les classes parentes en profondeur d'abord, de gauche à droite sans rechercher deux fois la même classe.
Girish Gupta
désolé mais je ne comprends pas pourquoi dans le premier cas c'est aller class B3mais dans le second cas c'est aller class Aaprèsclass B1
Vadim