Đây là một ví dụ tuyệt vời về lý do tại sao các __dunder__
phương thức không nên được sử dụng trực tiếp vì chúng thường không thay thế phù hợp cho các toán tử tương đương của chúng; ==
thay vào đó, bạn nên sử dụng toán tử để so sánh bằng, hoặc trong trường hợp đặc biệt này, khi kiểm tra None
, sử dụng is
(bỏ qua phần dưới của câu trả lời để biết thêm thông tin).
Bạn đã hoàn thành
None.__eq__('a')
# NotImplemented
Mà trả về NotImplemented
kể từ khi các loại được so sánh là khác nhau. Xem xét một ví dụ khác trong đó hai đối tượng với các loại khác nhau đang được so sánh theo kiểu này, chẳng hạn như 1
và 'a'
. Làm (1).__eq__('a')
cũng không đúng, và sẽ trở lại NotImplemented
. Cách đúng để so sánh hai giá trị này cho sự bình đẳng sẽ là
1 == 'a'
# False
Những gì xảy ra ở đây là
- Đầu tiên,
(1).__eq__('a')
được thử, mà trả về NotImplemented
. Điều này chỉ ra rằng hoạt động không được hỗ trợ, vì vậy
'a'.__eq__(1)
được gọi, cũng trả lại tương tự NotImplemented
. Vì thế,
- Các đối tượng được đối xử như thể chúng không giống nhau, và
False
được trả lại.
Đây là một MCVE nhỏ xinh bằng cách sử dụng một số lớp tùy chỉnh để minh họa điều này xảy ra:
class A:
def __eq__(self, other):
print('A.__eq__')
return NotImplemented
class B:
def __eq__(self, other):
print('B.__eq__')
return NotImplemented
class C:
def __eq__(self, other):
print('C.__eq__')
return True
a = A()
b = B()
c = C()
print(a == b)
# A.__eq__
# B.__eq__
# False
print(a == c)
# A.__eq__
# C.__eq__
# True
print(c == a)
# C.__eq__
# True
Tất nhiên, điều đó không giải thích tại sao hoạt động trở lại đúng. Điều này là bởi vì NotImplemented
thực sự là một giá trị trung thực:
bool(None.__eq__("a"))
# True
Giống như,
bool(NotImplemented)
# True
Để biết thêm thông tin về những giá trị nào được coi là trung thực và giả mạo, hãy xem phần tài liệu về Kiểm tra giá trị thật , cũng như câu trả lời này . Điều đáng chú ý ở đây NotImplemented
là sự thật, nhưng nó sẽ là một câu chuyện khác nếu lớp xác định một __bool__
hoặc __len__
phương thức trả về False
hoặc 0
tương ứng.
Nếu bạn muốn chức năng tương đương của ==
toán tử, sử dụng operator.eq
:
import operator
operator.eq(1, 'a')
# False
Tuy nhiên, như đã đề cập trước đó, đối với kịch bản cụ thể này , nơi bạn đang kiểm tra None
, hãy sử dụng is
:
var = 'a'
var is None
# False
var2 = None
var2 is None
# True
Tương đương chức năng của điều này là sử dụng operator.is_
:
operator.is_(var2, None)
# True
None
là một đối tượng đặc biệt và chỉ có 1 phiên bản tồn tại trong bộ nhớ tại bất kỳ thời điểm nào. IOW, nó là đơn lẻ duy nhất của NoneType
lớp (nhưng cùng một đối tượng có thể có bất kỳ số lượng tài liệu tham khảo nào). Các hướng dẫn PEP8 làm cho điều này rõ ràng:
Việc so sánh với các singletons như vậy None
phải luôn luôn được thực hiện với is
hoặc
is not
, không bao giờ là các toán tử đẳng thức.
Tóm lại, đối với các singletons như None
, một kiểm tra tham chiếu với is
phù hợp hơn, mặc dù cả hai ==
và is
sẽ hoạt động tốt.