Thứ tự phân giải phương thức Python 2.3

Ghi chú

Đây là một tài liệu lịch sử, được cung cấp dưới dạng phụ lục cho tài liệu chính thức. Thứ tự phân giải phương thức được thảo luận ở đây là introduced trong Python 2.3, nhưng nó vẫn được sử dụng trong các phiên bản sau -- bao gồm cả Python 3.

Bởi Michele Simionato.

Tóm tắt:

This document is intended for Python programmers who want to understand the C3 Method Resolution Order used in Python 2.3. Although it is not intended for newbies, it is quite pedagogical with many worked out examples. I am not aware of other publicly available documents with the same scope, therefore it should be useful.

Tuyên bố từ chối trách nhiệm:

I donate this document to the Python Software Foundation, under the Python 2.3 license. As usual in these circumstances, I warn the reader that what follows nên be correct, but I don't give any warranty. Use it at your own risk and peril!

Lời cảm ơn:

All the people of the Python mailing list who sent me their support. Paul Foley who pointed out various imprecisions and made me to add the part on local precedence ordering. David Goodger for help with the formatting in reStructuredText. David Mertz for help with the editing. Finally, Guido van Rossum who enthusiastically added this document to the official Python 2.3 home-page.

Sự khởi đầu

Felix qui potuit rerum cognoscere causas -- Virgilius

Mọi thứ bắt đầu với một bài đăng của Samuele Pedroni trên danh sách gửi thư phát triển Python [1]. Trong bài đăng của mình, Samuele đã chỉ ra rằng thứ tự phân giải phương thức Python 2.2 không hề đơn điệu và anh ấy đã đề xuất thay thế nó bằng thứ tự phân giải phương thức C3. Guido đồng ý với lập luận của mình và do đó hiện tại Python 2.3 sử dụng C3. Bản thân phương pháp C3 không liên quan gì đến Python, vì nó được phát minh bởi những người làm việc với Dylan và nó được mô tả trong một bài báo dành cho những người nói ngọng [2]. Bài viết hiện tại đưa ra một cuộc thảo luận (hy vọng) có thể đọc được về thuật toán C3 dành cho những người dùng Pythonista muốn hiểu lý do của sự thay đổi.

Trước hết, hãy để tôi chỉ ra rằng những gì tôi sắp nói chỉ áp dụng cho new style classes được giới thiệu trong Python 2.2: classic classes duy trì thứ tự phân giải phương thức cũ, độ sâu trước rồi từ trái sang phải. Do đó, không có sự phá vỡ mã cũ đối với các lớp cổ điển; và ngay cả khi về nguyên tắc có thể xảy ra hiện tượng phá mã đối với các lớp kiểu mới của Python 2.2, thì trên thực tế, các trường hợp trong đó thứ tự phân giải C3 khác với thứ tự phân giải của phương thức Python 2.2 rất hiếm đến mức không thể phá mã thực sự. Vì vậy:

Don't be scared!

Hơn nữa, trừ khi bạn tận dụng triệt để tính đa kế thừa và bạn có hệ thống phân cấp không tầm thường, bạn không cần phải hiểu thuật toán C3 và bạn có thể dễ dàng bỏ qua bài viết này. Mặt khác, nếu bạn thực sự muốn biết tính đa kế thừa hoạt động như thế nào thì bài viết này là dành cho bạn. Tin tốt là mọi thứ không phức tạp như bạn mong đợi.

Hãy để tôi bắt đầu với một số định nghĩa cơ bản.

  1. Với một lớp C trong một hệ thống phân cấp đa kế thừa phức tạp, việc chỉ định thứ tự các phương thức được ghi đè là một nhiệm vụ không hề đơn giản, tức là chỉ định thứ tự của các phương thức tổ tiên của C.

  2. Danh sách tổ tiên của lớp C, bao gồm cả chính lớp đó, được sắp xếp từ tổ tiên gần nhất đến xa nhất, được gọi là danh sách ưu tiên lớp hoặc linearization của C.

  3. Method Resolution Order (MRO) là tập hợp các quy tắc xây dựng tuyến tính hóa. Trong tài liệu Python, thành ngữ "the MRO of C" cũng được sử dụng như một từ đồng nghĩa với việc tuyến tính hóa lớp C.

  4. Ví dụ, trong trường hợp phân cấp thừa kế đơn, nếu C là lớp con của C1 và C1 là lớp con của C2, thì việc tuyến tính hóa C chỉ đơn giản là danh sách [C, C1, C2]. Tuy nhiên, với nhiều hệ thống phân cấp kế thừa, việc xây dựng tuyến tính hóa sẽ cồng kềnh hơn, vì việc xây dựng tuyến tính hóa tôn trọng local precedence orderingmonotonicity sẽ khó khăn hơn.

  5. Tôi sẽ thảo luận về thứ tự ưu tiên cục bộ sau, nhưng tôi có thể đưa ra định nghĩa về tính đơn điệu ở đây. Một MRO là đơn điệu khi điều sau đây đúng: if C1 precedes C2 in the linearization of C, then C1 precedes C2 in the linearization of any subclass of C. Mặt khác, hoạt động vô hại để tạo ra một lớp mới có thể thay đổi thứ tự phân giải của các phương thức, có khả năng gây ra các lỗi rất tinh vi. Ví dụ nơi điều này xảy ra sẽ được hiển thị sau.

  6. Không phải tất cả các lớp đều chấp nhận tuyến tính hóa. Có những trường hợp, trong các hệ thống phân cấp phức tạp, không thể rút ra một lớp sao cho việc tuyến tính hóa của nó tôn trọng tất cả các thuộc tính mong muốn.

Ở đây tôi đưa ra một ví dụ về tình huống này. Hãy xem xét hệ thống phân cấp

>>> O = object
>>> class X(O): pass
>>> class Y(O): pass
>>> class A(X,Y): pass
>>> class B(Y,X): pass

có thể được biểu diễn bằng biểu đồ kế thừa sau, trong đó tôi đã biểu thị bằng O lớp object, đây là điểm khởi đầu của bất kỳ hệ thống phân cấp nào cho các lớp kiểu mới:

-----------
|           |
|    O      |
|  /   \    |
 - X Y /
   |  / | /
   | /  |/
   A B
   \ /
     ?

Trong trường hợp này, không thể tạo ra một lớp C mới từ A và B, vì X có trước Y trong A, nhưng Y có trước X trong B, do đó thứ tự phân giải phương thức sẽ không rõ ràng trong C.

Python 2.3 đưa ra một ngoại lệ trong tình huống này (TypeError: MRO xung đột giữa các cơ sở Y, X) cấm lập trình viên ngây thơ tạo ra các hệ thống phân cấp mơ hồ. Thay vào đó, Python 2.2 không đưa ra ngoại lệ mà chọn thứ tự ad hoc (CABXYO trong trường hợp này).

Thứ tự giải quyết phương pháp C3

Hãy để tôi giới thiệu một vài ký hiệu đơn giản sẽ hữu ích cho cuộc thảo luận sau đây. Tôi sẽ sử dụng ký hiệu phím tắt

C1 C2... CN

để chỉ ra danh sách các lớp [C1, C2, ... , CN].

Zz000zz của danh sách là thành phần đầu tiên của nó:

đầu = C1

trong khi tail là phần còn lại của danh sách:

đuôi = C2...CN.

Tôi cũng sẽ sử dụng ký hiệu:

C +(C1 C2...CN) = C C1 C2...CN

để biểu thị tổng của các danh sách [C] + [C1, C2, ... ,CN].

Bây giờ tôi có thể giải thích cách MRO hoạt động trong Python 2.3.

Hãy xem xét một lớp C trong hệ thống phân cấp đa kế thừa, trong đó C kế thừa từ các lớp cơ sở B1, B2, ... , BN. Chúng tôi muốn tính toán tuyến tính hóa L[C] của lớp C. Quy tắc như sau:

the linearization of C is the sum of C plus the merge of the linearizations of the parents and the list of the parents.

Trong ký hiệu tượng trưng:

L[C(B1 ... BN)] = C + hợp nhất(L[B1] ... L[BN], B1 ... BN)

Cụ thể, nếu C là lớp object, không có cha, thì việc tuyến tính hóa là không đáng kể:

L[đối tượng] = đối tượng.

Tuy nhiên, nói chung người ta phải tính toán việc hợp nhất theo công thức sau:

take the head of the first list, i.e L[B1][0]; if this head is not in the tail of any of the other lists, then add it to the linearization of C and remove it from the lists in the merge, otherwise look at the head of the next list and take it, if it is a good head. Then repeat the operation until all the class are removed or it is impossible to find good heads. In this case, it is impossible to construct the merge, Python 2.3 will refuse to create the class C and will raise an exception.

Đơn thuốc này đảm bảo rằng hoạt động hợp nhất preserves sẽ giữ nguyên thứ tự, nếu thứ tự đó có thể được giữ nguyên. Mặt khác, nếu thứ tự không thể được giữ nguyên (như trong ví dụ về sự bất đồng nghiêm trọng về thứ tự đã thảo luận ở trên) thì việc hợp nhất không thể được tính toán.

Việc tính toán hợp nhất là không đáng kể nếu C chỉ có một cha (kế thừa đơn); trong trường hợp này:

L[C(B)] = C + hợp nhất(L[B],B) = C + L[B]

Tuy nhiên, trong trường hợp có nhiều kế thừa thì mọi thứ sẽ cồng kềnh hơn và tôi không mong đợi bạn có thể hiểu quy tắc mà không cần một vài ví dụ ;-)

Ví dụ

Ví dụ đầu tiên. Hãy xem xét hệ thống phân cấp sau:

>>> O = object
>>> class F(O): pass
>>> class E(O): pass
>>> class D(O): pass
>>> class C(D,F): pass
>>> class B(D,E): pass
>>> class A(B,C): pass

Trong trường hợp này, đồ thị thừa kế có thể được vẽ như sau:

6
                         ---
Cấp 3 | O | (tổng quát hơn)
                      / --- \
                     / |    \                      |
                    / |     \                     |
                   / |      \                    |
                  --- --- --- |
Cấp 2 3 | D | 4| E | | F | 5 |
                  --- --- --- |
                   \ \ _ / |                   |
                    \ / \ _ |                   |
                     \ / \ |                   |
                      --- --- |
Cấp 1 1 | B | | C | 2 |
                      --- --- |
                        \ / |
                         \ / \ /
                           ---
Cấp 0 0 | A | (chuyên biệt hơn)
                           ---

Việc tuyến tính hóa của O, D, E và F là tầm thường:

L[O] = O
L[D] = D O
L[E] = EO
L[F] = FO

Sự tuyến tính hóa của B có thể được tính như sau:

L[B] = B + hợp nhất(DO, EO, DE)

Chúng tôi thấy rằng D là một người đứng đầu tốt, do đó chúng tôi lấy nó và chúng tôi rút gọn để tính merge(O,EO,E). Bây giờ O không phải là đầu tốt vì nó nằm ở đuôi của chuỗi EO. Trong trường hợp này, quy tắc nói rằng chúng ta phải chuyển sang trình tự tiếp theo. Khi đó ta thấy E là một người đứng đầu tốt; chúng tôi lấy nó và chúng tôi tính toán merge(O,O) cho kết quả O. Do đó:

L[B] = B D E O

Sử dụng quy trình tương tự người ta sẽ tìm thấy:

L[C] = C + hợp nhất(DO,FO,DF)
     = C + D + hợp nhất(O,FO,F)
     = C + D + F + hợp nhất(O,O)
     = C D F O

Bây giờ chúng ta có thể tính toán:

L[A] = A + hợp nhất(BDEO,CDFO,BC)
     = A + B + hợp nhất(DEO,CDFO,C)
     = A + B + C + hợp nhất(DEO,DFO)
     = A + B + C + D + hợp nhất(EO,FO)
     = A + B + C + D + E + hợp nhất(O,FO)
     = A + B + C + D + E + F + hợp nhất(O,O)
     = A B C D E F O

Trong ví dụ này, việc tuyến tính hóa được sắp xếp theo một cách khá hay theo mức độ kế thừa, theo nghĩa là các cấp độ thấp hơn (tức là các lớp chuyên biệt hơn) có mức độ ưu tiên cao hơn (xem biểu đồ kế thừa). Tuy nhiên, đây không phải là trường hợp chung.

Tôi để lại như một bài tập cho người đọc tính toán tuyến tính hóa cho ví dụ thứ hai của tôi:

>>> O = object
>>> class F(O): pass
>>> class E(O): pass
>>> class D(O): pass
>>> class C(D,F): pass
>>> class B(E,D): pass
>>> class A(B,C): pass

Sự khác biệt duy nhất với ví dụ trước là sự thay đổi B(D,E) --> B(E,D); tuy nhiên ngay cả một sửa đổi nhỏ như vậy cũng thay đổi hoàn toàn thứ tự của hệ thống phân cấp:

6
                          ---
Cấp 3 | O |
                       / --- \
                      / |    \
                     / |     \
                    / |      \
                  --- --- ---
Cấp 2 2 | E | 4 | D | | F | 5
                  --- --- ---
                   \ / \ /
                    \ / \ /
                     \ / \ /
                      --- ---
Cấp 1 1 | B | | C | 3
                      --- ---
                       \ /
                        \ /
                          ---
Cấp 0 0 | A |
                          ---

Lưu ý rằng lớp E, ở cấp độ thứ hai của hệ thống phân cấp, đứng trước lớp C, nằm ở cấp độ đầu tiên của hệ thống phân cấp, tức là E chuyên biệt hơn C, ngay cả khi nó ở cấp độ cao hơn.

Một lập trình viên lười biếng có thể lấy MRO trực tiếp từ Python 2.2, vì trong trường hợp này nó trùng với quá trình tuyến tính hóa Python 2.3. Chỉ cần gọi phương thức mro() của lớp A là đủ:

>>> A.mro()
[<class 'A'>, <class 'B'>, <class 'E'>,
<class 'C'>, <class 'D'>, <class 'F'>,
<class 'object'>]

Cuối cùng, hãy để tôi xem xét ví dụ được thảo luận ở phần đầu tiên, liên quan đến sự bất đồng nghiêm trọng về trật tự. Trong trường hợp này, việc tính toán tuyến tính hóa của O, X, Y, A và B là rất đơn giản:

L[O] = 0
L[X] = XO
L[Y] = YO
L[A] = A X Y O
L[B] = B Y X O

Tuy nhiên, không thể tính toán tuyến tính hóa cho lớp C kế thừa từ A và B

L[C] = C + hợp nhất(AXYO, BYXO, AB)
     = C + A + hợp nhất(XYO, BYXO, B)
     = C + A + B + hợp nhất(XYO, YXO)

Tại thời điểm này, chúng ta không thể hợp nhất danh sách XYO và YXO, vì X nằm ở đuôi YXO trong khi Y nằm ở đuôi XYO: do đó không có đầu tốt và thuật toán C3 dừng lại. Python 2.3 phát sinh lỗi và từ chối tạo lớp C.

Lệnh giải quyết phương pháp sai

Một MRO là bad khi nó phá vỡ các thuộc tính cơ bản như thứ tự ưu tiên cục bộ và tính đơn điệu. Trong phần này, tôi sẽ chỉ ra rằng cả MRO cho các lớp cổ điển và MRO cho các lớp kiểu mới trong Python 2.2 đều tệ.

Bắt đầu với thứ tự ưu tiên cục bộ sẽ dễ dàng hơn. Hãy xem xét ví dụ sau:

>>> F=type('Food',(),{'remember2buy':'spam'})
>>> E=type('Eggs',(F,),{'remember2buy':'eggs'})
>>> G=type('GoodFood',(F,E),{}) # under Python 2.3 this is an error!

với sơ đồ kế thừa

ồ
             |
(mua thư rác) F
             | \
             | E (mua trứng)
             | /
             G

      (mua trứng hoặc thư rác?)

Chúng tôi thấy rằng lớp G kế thừa từ F và E, với F before E: do đó, chúng tôi mong đợi thuộc tính G.remember2buy được kế thừa bởi F.remember2buy chứ không phải bởi E.remember2buy: tuy nhiên Python 2.2 cung cấp

>>> G.remember2buy
'eggs'

Đây là sự vi phạm thứ tự ưu tiên cục bộ vì thứ tự trong danh sách ưu tiên cục bộ, tức là danh sách cha mẹ của G, không được giữ nguyên trong quá trình tuyến tính hóa Python 2.2 của G

L[G,P22]= G E F đối tượng # F *follows* E

Người ta có thể lập luận rằng lý do tại sao F theo sau E trong quá trình tuyến tính hóa Python 2.2 là vì F kém chuyên biệt hơn E, vì F là siêu lớp của E; tuy nhiên việc phá vỡ thứ tự ưu tiên cục bộ khá không trực quan và dễ xảy ra lỗi. Điều này đặc biệt đúng vì nó khác với các lớp kiểu cũ:

>>> class F: remember2buy='spam'
>>> class E(F): remember2buy='eggs'
>>> class G(F,E): pass
>>> G.remember2buy
'spam'

Trong trường hợp này, MRO là GFEF và thứ tự ưu tiên cục bộ được giữ nguyên.

Theo nguyên tắc chung, nên tránh các hệ thống phân cấp như hệ thống trước đó vì không rõ liệu F có nên ghi đè lên E hay ngược lại hay không. Python 2.3 giải quyết sự mơ hồ bằng cách đưa ra một ngoại lệ trong việc tạo lớp G, ngăn chặn hiệu quả việc lập trình viên tạo ra các hệ thống phân cấp mơ hồ. Nguyên nhân là do thuật toán C3 bị lỗi khi hợp nhất:

hợp nhất(FO,EFO,FE)

không thể tính được, vì F ở đuôi EFO và E ở đuôi FE.

Giải pháp thực sự là thiết kế một hệ thống phân cấp rõ ràng, tức là lấy G từ E và F (cụ thể hơn trước) chứ không phải từ F và E; trong trường hợp này, MRO chắc chắn là GEF.

ồ
           |
           F (thư rác)
         / |
(trứng) E |
         \ |
           G
             (trứng, không còn nghi ngờ gì nữa)

Python 2.3 buộc người lập trình phải viết các hệ thống phân cấp tốt (hoặc ít nhất là ít xảy ra lỗi hơn).

Về một lưu ý liên quan, hãy để tôi chỉ ra rằng thuật toán Python 2.3 đủ thông minh để nhận ra những lỗi rõ ràng, chẳng hạn như sự trùng lặp của các lớp trong danh sách cha mẹ:

>>> class A(object): pass
>>> class C(A,A): pass # error
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: duplicate base class A

Python 2.2 (cho cả lớp cổ điển và lớp kiểu mới) trong tình huống này sẽ không đưa ra bất kỳ ngoại lệ nào.

Cuối cùng, tôi muốn chỉ ra hai bài học chúng ta đã học được từ ví dụ này:

  1. bất chấp tên gọi, MRO xác định thứ tự phân giải của các thuộc tính, không chỉ của các phương thức;

  2. món ăn mặc định của Pythonistas là thư rác! (nhưng bạn đã biết điều đó ;-)

Sau khi đã thảo luận về vấn đề thứ tự ưu tiên cục bộ, bây giờ tôi hãy xem xét vấn đề đơn điệu. Mục tiêu của tôi là chứng tỏ rằng cả MRO dành cho các lớp cổ điển cũng như lớp kiểu mới của Python 2.2 đều không đơn điệu.

Để chứng minh rằng MRO cho các lớp cổ điển là không đơn điệu là khá tầm thường, chỉ cần nhìn vào sơ đồ hình thoi là đủ:

C
  / \
 / \
A B
 \ /
  \ /
   D

Người ta dễ dàng nhận ra sự không nhất quán:

L[B,P21] = B C # B đứng trước C : Phương pháp của B thắng
L[D,P21] = D A C B C # B tuân theo C : Phương pháp của C thắng!

Mặt khác, không có vấn đề gì với MRO Python 2.2 và 2.3, chúng cung cấp cả hai

L[D] = D A B C

Guido đã chỉ ra trong bài luận [3] của mình rằng MRO cổ điển không quá tệ trong thực tế, vì người ta thường có thể tránh kim cương cho các lớp cổ điển. Nhưng tất cả các lớp kiểu mới đều kế thừa từ object, do đó kim cương là không thể tránh khỏi và sự không nhất quán xuất hiện trong mọi biểu đồ đa kế thừa.

Tính năng MRO của Python 2.2 khiến việc phá bỏ tính đơn điệu trở nên khó khăn nhưng không phải là không thể. Ví dụ sau đây, ban đầu được cung cấp bởi Samuele Pedroni, cho thấy MRO của Python 2.2 không đơn điệu:

>>> class A(object): pass
>>> class B(object): pass
>>> class C(object): pass
>>> class D(object): pass
>>> class E(object): pass
>>> class K1(A,B,C): pass
>>> class K2(D,B,E): pass
>>> class K3(D,A):   pass
>>> class Z(K1,K2,K3): pass

Dưới đây là các tuyến tính hóa theo C3 MRO (bạn đọc nên kiểm chứng các tuyến tính hóa này như một bài tập và vẽ sơ đồ kế thừa ;-):

L[A] = A O
L[B] = B Ô
L[C] = C O
L[D] = D O
L[E] = EO
L[K1]= K1 A B C O
L[K2]= K2 D B E O
L[K3]= K3 D A O
L[Z] = Z K1 K2 K3 D A B C E O

Python 2.2 cung cấp chính xác các cách tuyến tính hóa giống nhau cho A, B, C, D, E, K1, K2 và K3, nhưng tuyến tính hóa khác cho Z

L[Z,P22] = Z K1 K3 A K2 D B C E O

Rõ ràng rằng sự tuyến tính hóa này là wrong, vì A xuất hiện trước D trong khi trong tuyến tính hóa của K3 A xuất hiện after D. Nói cách khác, trong các phương thức K3 dẫn xuất bởi các phương thức ghi đè D dẫn xuất bởi A, nhưng trong Z, vẫn là một lớp con của K3, các phương thức dẫn xuất bởi các phương thức ghi đè A dẫn xuất bởi D! Đây là sự vi phạm tính đơn điệu. Hơn nữa, việc tuyến tính hóa Z trong Python 2.2 cũng không nhất quán với thứ tự ưu tiên cục bộ, vì danh sách ưu tiên cục bộ của lớp Z là [K1, K2, K3] (K2 đứng trước K3), trong khi tuyến tính hóa Z K2 follows K3. Những vấn đề này giải thích tại sao quy tắc 2.2 bị loại bỏ để thay thế cho quy tắc C3.

Sự kết thúc

Phần này dành cho những độc giả thiếu kiên nhẫn, đã bỏ qua tất cả các phần trước và nhảy ngay đến phần cuối. Phần này cũng dành cho những lập trình viên lười biếng, những người không muốn rèn luyện trí não của mình. Cuối cùng, nó dành cho lập trình viên có một số tính kiêu ngạo, nếu không thì họ sẽ không đọc một bài báo về thứ tự phân giải phương pháp C3 trong nhiều hệ thống phân cấp kế thừa ;-) Ba đức tính này kết hợp lại với nhau (và not riêng) xứng đáng nhận được giải thưởng: giải thưởng là một tập lệnh Python 2.2 ngắn cho phép bạn tính toán 2,3 MRO mà không gây rủi ro cho bộ não của bạn. Chỉ cần thay đổi dòng cuối cùng để chơi với các ví dụ khác nhau mà tôi đã thảo luận trong bài viết này.:

#<mro.py>

"""Thuật toán C3 của Samuele Pedroni (với khả năng đọc được nâng cao bởi tôi)."""

lớp __metaclass__(loại):
    "Tất cả các lớp đều được sửa đổi một cách siêu hình để được in đẹp mắt"
    __repr__ = lambda cls: cls.__name__

lớp ex_2:
    "Bất đồng nghiêm trọng về trật tự" #From Guido
    lớp O: đậu
    lớp X(O): đậu
    lớp Y(O): đậu
    hạng A(X,Y): đậu
    hạng B(Y,X): đậu
    thử:
        lớp Z(A,B): vượt qua #creates Z(A,B) trong Python 2.2
    ngoại trừ Lỗi Loại:
        không thể tạo pass # Z(A,B) bằng Python 2.3

lớp ex_5:
    "Ví dụ đầu tiên của tôi"
    lớp O: đậu
    lớp F(O): đậu
    lớp E(O): đậu
    lớp D(O): đậu
    lớp C(D,F): đậu
    lớp B(D,E): đậu
    hạng A(B,C): đậu

lớp ex_6:
    "Ví dụ thứ hai của tôi"
    lớp O: đậu
    lớp F(O): đậu
    lớp E(O): đậu
    lớp D(O): đậu
    lớp C(D,F): đậu
    lớp B(E,D): đậu
    hạng A(B,C): đậu

lớp ex_9:
    "Sự khác biệt giữa Python 2.2 MRO và C3" #From Samuele
    lớp O: đậu
    hạng A(O): đậu
    lớp B(O): đậu
    lớp C(O): đậu
    lớp D(O): đậu
    lớp E(O): đậu
    lớp K1(A,B,C): đậu
    lớp K2(D,B,E): đậu
    lớp K3(D,A): đậu
    lớp Z(K1,K2,K3): đạt

hợp nhất def(seqs):
    print '\n\nCPL[%s]=%s' % (seqs[0][0],seqs),
    độ phân giải = []; tôi=0
    trong khi 1:
      nonemptyseqs=[seq cho seq trong seqs nếu seq]
      nếu không phải nonemptyseqs: trả về res
      tôi+=1; print '\n',i,'round: ứng viên...',
      đối với seq trong nonemptyseqs: # find hợp nhất các ứng cử viên giữa các đầu seq
          cand = seq[0]; in '', cand,
          nothead=[s for s in nonemptyseqs if cand in s[1:]]
          nếu không  tiêu đề: cand=None #reject ứng cử viên
          khác: nghỉ
      nếu không can: raise "Phân cấp không nhất quán"
      res.append(cand)
      cho seq trong nonemptyseqs: # remove cand
          if seq[0] == cand: del seq[0]

def mro(C):
    "Tính danh sách ưu tiên lớp (mro) theo C3"
    return merge([[C]]+map(mro,C.__bases__)+[list(C.__bases__)])

def print_mro(C):
    print '\nMRO[%s]=%s' % (C,mro(C))
    print '\nP22 MRO[%s]=%s' % (C,C.mro())

print_mro(ex_9.Z)

#</mro.py>

Thế thôi mọi người ạ

tận hưởng !

Tài nguyên