Apa yang dilakukan “mro ()”?

125

Di django.utils.functional.py:

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

Saya tidak mengerti mro() . Apa fungsinya dan apa artinya "mro"?

zjm1126
sumber

Jawaban:

211

Ikuti...:

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

Selama kita memiliki satu warisan, __mro__hanyalah tuple dari: kelas, basisnya, basis basisnya, dan seterusnya hinggaobject (hanya bekerja untuk kelas gaya baru tentu saja).

Sekarang, dengan beberapa warisan ...:

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

... Anda juga mendapatkan jaminan bahwa, dalam __mro__, tidak ada kelas yang diduplikasi, dan tidak ada kelas setelah leluhurnya, kecuali bahwa kelas yang pertama kali masuk pada tingkat pewarisan berganda yang sama (seperti B dan C dalam contoh ini) ada di __mro__kiri ke kanan.

Setiap atribut yang Anda dapatkan pada instance kelas, bukan hanya metode, secara konseptual melihat ke atas __mro__, jadi, jika lebih dari satu kelas di antara leluhur menentukan nama itu, ini memberi tahu Anda di mana atribut akan ditemukan - di kelas pertama di yang __mro__yang mendefinisikan nama itu.

Alex Martelli
sumber
9
hai, alex, apakah ada perbedaan antara D .__ mro__ dan D.mro ().
zjm1126
26
mrodapat dikustomisasi oleh metaclass, dipanggil sekali pada inisialisasi kelas, dan hasilnya disimpan dalam __mro__- lihat docs.python.org/library/… .
Alex Martelli
23
Mengapa ini disebut urutan resolusi metode, bukan urutan resolusi atribut ?
Bentley4
1
Seperti yang dikatakan @Alex Martelli dan konten python-history.blogspot.com/2010/06/... , atribut mro harus ditambahkan ketika kelas baru digunakan, seperti hanya ketika Python 2.2 MRO dan Python 2.3 MRO (C3) digunakan.
andy
82

mro()singkatan dari Method Resolution Order. Ini mengembalikan daftar jenis kelas berasal, dalam urutan mereka mencari metode.

mro () atau __mro__ hanya berfungsi pada kelas gaya baru. Di python 3, mereka bekerja tanpa masalah. Tetapi dalam python 2 kelas-kelas tersebut perlu diwarisi dari object.

Ned Batchelder
sumber
10

Ini mungkin akan menunjukkan urutan resolusi.

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

dan tanggapan akan

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

Aturannya adalah kedalaman-pertama, yang dalam hal ini berarti D, B, A, C.

Python biasanya menggunakan urutan kedalaman-pertama saat mencari kelas yang diwariskan, tetapi ketika dua kelas mewarisi dari kelas yang sama, Python menghapus penyebutan pertama dari kelas itu dari mro.

Stryker
sumber
1
Jadi seperti yang Anda lihat, urutannya adalah D, B, A, C
Stryker
1
Apa yang Anda maksud dengan "Python menghapus penyebutan pertama dari kelas itu dari mro."?
Ruthvik Vaila
2
@ RuthvikVaila: Saya pikir bagian itu hanya dinyatakan dengan buruk. Dalam posting blog ini tentang sejarah Python, Guido van Rossum berkomentar (tentang skema MRO yang diperkenalkan dalam Python 2.2) "Jika ada kelas yang diduplikasi dalam pencarian ini, semua kecuali kejadian terakhir akan dihapus dari daftar MRO" (penekanan saya ). Ini menyiratkan bahwa itu dapat menghapus lebih dari sekedar "penyebutan" pertama dari kelas.
martineau
1

Urutan resolusi akan berbeda dalam warisan berlian.

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
sumber
tetapi mengapa resolusinya berbeda?
Vadim
Dalam skenario pewarisan berganda, atribut apa pun yang ditentukan dicari pertama kali di kelas saat ini. Jika tidak ditemukan, pencarian berlanjut ke kelas induk dengan mode kedalaman-pertama, kiri-kanan tanpa mencari kelas yang sama dua kali.
Girish Gupta
maaf tapi saya tidak mengerti mengapa dalam kasus pertama pergi ke class B3tetapi dalam kasus kedua itu pergi class Asetelahclass B1
Vadim