นี่เป็นตัวอย่างที่ดีว่าทำไม__dunder__ไม่ควรใช้วิธีการโดยตรงเนื่องจากเป็นวิธีที่ไม่เหมาะสมในการทดแทนตัวดำเนินการที่เทียบเท่า คุณควรใช้==โอเปอเรเตอร์แทนการเปรียบเทียบความเท่าเทียมกันหรือในกรณีพิเศษนี้เมื่อตรวจสอบNoneใช้is(ข้ามไปที่ด้านล่างของคำตอบสำหรับข้อมูลเพิ่มเติม)
คุณทำไปแล้ว
None.__eq__('a')
# NotImplemented
ซึ่งผลตอบแทนNotImplementedตั้งแต่ประเภทการเปรียบเทียบแตกต่างกัน พิจารณาอีกตัวอย่างหนึ่งที่วัตถุสองชนิดที่แตกต่างกันถูกเปรียบเทียบแบบนี้เช่นและ1 'a'การทำเช่นนี้ยังไม่ถูกต้องและจะกลับมา(1).__eq__('a') NotImplementedวิธีที่ถูกต้องในการเปรียบเทียบค่าทั้งสองนี้เพื่อความเท่าเทียมกันคือ
1 == 'a'
# False
เกิดอะไรขึ้นที่นี่คือ
- ครั้งแรกที่มีการพยายามที่ผลตอบแทน
(1).__eq__('a') NotImplementedสิ่งนี้บ่งชี้ว่าการดำเนินการไม่ได้รับการสนับสนุนดังนั้น
'a'.__eq__(1)NotImplementedเรียกว่าซึ่งผลตอบแทนที่เดียวกัน ดังนั้น,
- วัตถุได้รับการปฏิบัติราวกับว่าพวกเขาจะไม่เหมือนกันและ
Falseจะถูกส่งกลับ
นี่เป็น MCVE เล็ก ๆ น้อย ๆ ที่ดีที่ใช้คลาสที่กำหนดเองเพื่อแสดงให้เห็นว่าสิ่งนี้เกิดขึ้นได้อย่างไร:
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
แน่นอนว่าไม่ได้อธิบายว่าทำไมการดำเนินการจึงกลับเป็นจริง เพราะนี่คือNotImplementedคุณค่าที่แท้จริง:
bool(None.__eq__("a"))
# True
เหมือนกับ,
bool(NotImplemented)
# True
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับค่าใดที่ถือว่าเป็นความจริงและเป็นเท็จให้ดูส่วนเอกสารเกี่ยวกับการทดสอบความจริงและคำตอบนี้ มันคุ้มค่าที่จะสังเกตว่านี่NotImplementedคือความจริง แต่มันจะเป็นเรื่องที่แตกต่างกันเมื่อชั้นเรียนกำหนด__bool__หรือ__len__วิธีการที่กลับมาFalseหรือ0ตามลำดับ
หากคุณต้องการฟังก์ชั่นที่เทียบเท่ากับ==ผู้ปฏิบัติงานให้ใช้operator.eq:
import operator
operator.eq(1, 'a')
# False
อย่างไรก็ตามตามที่กล่าวไว้ก่อนหน้านี้สำหรับสถานการณ์เฉพาะที่คุณกำลังตรวจสอบให้Noneใช้is:
var = 'a'
var is None
# False
var2 = None
var2 is None
# True
ฟังก์ชันที่เทียบเท่ากับสิ่งนี้คือการใช้operator.is_:
operator.is_(var2, None)
# True
Noneเป็นวัตถุพิเศษและมีเพียง 1 รุ่นเท่านั้นที่อยู่ในหน่วยความจำ ณ จุดใดก็ได้ IOW มันเป็นซิงเกิลตันของNoneTypeคลาส (แต่วัตถุเดียวกันอาจมีหมายเลขอ้างอิงใด ๆ ) แนวทาง PEP8ทำให้เรื่องนี้อย่างชัดเจน:
การเปรียบเทียบกับซิงเกิลตันNoneควรทำด้วยเสมอisหรือ
is notไม่ควรใช้ตัวดำเนินการที่เท่าเทียมกัน
โดยสรุปสำหรับ singletons เช่นNoneการตรวจสอบการอ้างอิงมีisความเหมาะสมมากขึ้นแม้ว่าทั้งสอง==และisจะทำงานได้ดี