Câu trả lời ngắn gọn là họ muốn cho phép AB
ghi đè hành vi từ A
. Python không thể gọi AB.__lt__(a, ab)
, vì a
có thể không hợp lệ self
cho phương thức AB
, do đó, thay vào đó, nó gọi là AB.__gt__(ab, a)
, hợp lệ.
Câu trả lời dài phức tạp hơn một chút.
Theo các tài liệu cho rich comparison operators:
Không có phiên bản hoán đổi đối số của các phương pháp này (được sử dụng khi lập luận trái không hỗ trợ các hoạt động nhưng lập luận đúng không); thay vào đó, __lt__()
và __gt__()
là sự phản ánh của nhau, __le__()
và __ge__()
là sự phản ánh của nhau và __eq__()
và __ne__()
là sự phản ánh của riêng chúng.
Nói cách khác, x <= y
sẽ gọi y.__ge__(x)
chính xác các trường hợp tương tự nơi x+y
sẽ gọi y.__radd__(x)
. Để so sánh:
>>> class X(object):
... def __add__(self, other):
... print('X.add')
>>> class Y(object):
... def __radd__(self, other):
... print('Y.radd')
>>> class XY(X, Y):
... pass
>>> x, xy = X(), XY()
>>> x + xy
Y.radd
Theo các tài liệu cho reflected operators:
Những phương pháp này được gọi là để thực hiện các phép tính số học nhị phân ... với phản ánh (hoán đổi) toán hạng. Các chức năng này chỉ được gọi nếu toán hạng bên trái không hỗ trợ thao tác tương ứng và các toán hạng có các loại khác nhau…
Lưu ý: Nếu kiểu toán hạng phải là phân lớp của kiểu toán hạng trái và phân lớp đó cung cấp phản ánh phương thức cho phép toán, phương thức này sẽ được gọi trước phương thức không được phản xạ của toán hạng trái. Hành vi này cho phép các lớp con ghi đè hoạt động của tổ tiên của họ.
Vì vậy, bởi vì XY
là một lớp con của X
, XY.__radd__
được ưu tiên hơn X.__add__
. Và, tương tự, vì AB
là một phân lớp của A
, AB.__ge__
được ưu tiên hơn A.__le__
.
Điều này có lẽ phải được ghi lại tốt hơn.Để tìm ra, bạn phải bỏ qua parenthetical "được sử dụng khi đối số bên trái không hỗ trợ thao tác nhưng đối số đúng", bạn cần tìm kiếm các toán tử hoán đổi thông thường (không có liên kết hoặc thậm chí là đề cập đến) , ở đây), sau đó bỏ qua từ ngữ có nội dung "Các hàm này chỉ được gọi nếu toán hạng bên trái không hỗ trợ thao tác tương ứng" và xem "Ghi chú", điều này mâu thuẫn với những gì ở trên… Cũng lưu ý rằng các tài liệu nói rõ ràng, " Không có mối quan hệ ngụ ý giữa các toán tử so sánh ", chỉ có một đoạn trước khi mô tả các trường hợp hoán đổi, trong đó ngụ ý chính xác các mối quan hệ như vậy ...
Cuối cùng, trường hợp này có vẻ lạ, vì tự mình ghi đè __ge__
, chỉ thừa kế nó từ B
, không biết gì về A
và không liên quan đến nó. Có lẽ B
không có ý định có các lớp con của nó ghi đè hành vi của A
. Nhưng nếu B
được sử dụng như một bản mix cho các lớp học có kích thước A
, có thể là sẽ có ý định ghi đè chính xác như vậy. Và ở mức độ nào đó, quy tắc có lẽ đã đủ phức tạp mà không đi vào nơi mà mỗi phương pháp đến từ MRO. Bất kể lý do gì, nơi mà __ge__
xuất phát từ không liên quan; nếu nó có trên lớp con, nó sẽ được gọi.
Đối với bổ sung cuối cùng, câu hỏi của bạn, "Tôi có thể làm gì để có được __le__
gọi thay vì __ge__
??" ... tốt, bạn thực sự không thể, bất kỳ hơn bạn có thể nhận X.__add__
gọi thay vì XY.__radd__
. Tất nhiên bạn luôn có thể triển khai AB.__ge__
(hoặc XY.__radd__
) gọi A.__le__
(hoặc X.__add__
), nhưng có lẽ dễ dàng hơn khi chỉ thực hiện AB.__ge__
theo cách như vậy với đối số khác của nó ở vị trí đầu tiên A
. Ngoài ra, bạn có thể loại bỏ các thừa kế và tìm một số cách khác để mô hình bất cứ điều gì bạn đã được mô hình theo cách đó. Hoặc bạn có thể gọi một cách rõ ràng a.__le__(ab)
thay vì a<=ab
. Nhưng nếu không, nếu bạn thiết kế các lớp học theo cách tận dụng "không có mối quan hệ ngụ ý" để làm điều gì đó kỳ lạ, bạn đã bị các tài liệu lừa dối và sẽ phải thiết kế lại chúng bằng cách nào đó.
Tại sao bạn có tất cả những thứ phức tạp với hàm 'make', khi bạn chỉ có thể làm' a = A() 'và' ab = AB() 'và nhận các đối tượng giống nhau? (Tôi có thể thấy lý do tại sao nó có thể hữu ích trong chương trình thực sự của bạn, nhưng nó không ảnh hưởng đến ví dụ này, và nó có lẽ sẽ ném ra một số người muốn điều tra và hy vọng câu trả lời.) – abarnert
Thật vậy, phiên bản đơn giản bạn đề xuất cũng có hành vi tương tự. Bây giờ sửa chữa. – user474491