इस साधारण समस्या पर विचार करें:
class Number:
def __init__(self, number):
self.number = number
n1 = Number(1)
n2 = Number(1)
n1 == n2 # False -- oops
इसलिए, डिफ़ॉल्ट रूप से पायथन तुलनात्मक संचालन के लिए वस्तु पहचानकर्ताओं का उपयोग करता है:
id(n1) # 140400634555856
id(n2) # 140400634555920
__eq__
फ़ंक्शन को ओवरराइड करने से समस्या का समाधान होता है:
def __eq__(self, other):
"""Overrides the default implementation"""
if isinstance(other, Number):
return self.number == other.number
return False
n1 == n2 # True
n1 != n2 # True in Python 2 -- oops, False in Python 3
में अजगर 2 , हमेशा ओवरराइड करने के लिए याद __ne__
के साथ-साथ समारोह, के रूप में प्रलेखन कहता है:
तुलना ऑपरेटरों के बीच कोई निहित संबंध नहीं हैं। सत्य का x==y
अर्थ x!=y
असत्य नहीं है । तदनुसार, परिभाषित करते समय __eq__()
, एक को भी परिभाषित करना चाहिए __ne__()
ताकि ऑपरेटर अपेक्षा के अनुरूप व्यवहार करेंगे।
def __ne__(self, other):
"""Overrides the default implementation (unnecessary in Python 3)"""
return not self.__eq__(other)
n1 == n2 # True
n1 != n2 # False
में अजगर 3 , अब यह आवश्यक है, के रूप में प्रलेखन कहता है:
डिफ़ॉल्ट रूप से, जब तक यह नहीं होता है तब तक परिणाम को दर्शाता __ne__()
है __eq__()
और इनवर्ट करता है NotImplemented
। तुलना ऑपरेटरों के बीच कोई अन्य निहित संबंध नहीं हैं, उदाहरण के लिए, सच्चाई का (x<y or x==y)
मतलब नहीं है x<=y
।
लेकिन वह हमारी सभी समस्याओं को हल नहीं करता है। चलो एक उपवर्ग जोड़ें:
class SubNumber(Number):
pass
n3 = SubNumber(1)
n1 == n3 # False for classic-style classes -- oops, True for new-style classes
n3 == n1 # True
n1 != n3 # True for classic-style classes -- oops, False for new-style classes
n3 != n1 # False
नोट: अजगर 2 में दो प्रकार की कक्षाएं हैं:
क्लासिक-शैली (या पुरानी-शैली ) कक्षाएं, जोइनहेरिट नहींobject
करती हैं और जिन्हें घोषित किया जाता हैclass A:
,class A():
याclass A(B):
जहांB
एक क्लासिक-शैली वर्ग है;
नई शैली की कक्षाएं, जो इनहेरिट करती हैंobject
और जिन्हेंनए शैली वर्ग केरूप मेंclass A(object)
याclass A(B):
जहांघोषित किया जाताB
है। पायथन 3 में केवल नई-शैली की कक्षाएं हैं जिन्हें घोषित किया गया हैclass A:
,class A(object):
याclass A(B):
।
क्लासिक-शैली की कक्षाओं के लिए, एक तुलना ऑपरेशन हमेशा पहले ऑपरेंड की विधि को कहता है, जबकि नई-शैली की कक्षाओं के लिए, यह हमेशा ऑपरेंड के आदेश की परवाह किए बिना , उप-वर्ग ऑपरेंड की विधि को कॉल करता है ।
यहाँ है, अगर Number
एक क्लासिक शैली वर्ग है:
n1 == n3
कॉल करता है n1.__eq__
;
n3 == n1
कॉल करता है n3.__eq__
;
n1 != n3
कॉल करता है n1.__ne__
;
n3 != n1
कॉल करता है n3.__ne__
।
और अगर Number
एक नई शैली वर्ग है:
- दोनों
n1 == n3
और n3 == n1
बुलाओ n3.__eq__
;
- दोनों
n1 != n3
और n3 != n1
बुलाओ n3.__ne__
।
पायथन 2 क्लासिक शैली की कक्षाओं के लिए ==
और !=
ऑपरेटरों के गैर-कम्यूटेटिविटी मुद्दे को ठीक करने के लिए, __eq__
और __ne__
तरीकों को उस NotImplemented
मूल्य को वापस करना चाहिए जब एक ऑपरेंड प्रकार समर्थित नहीं है। प्रलेखन को परिभाषित करता है NotImplemented
के रूप में मूल्य:
यदि वे प्रदान किए गए ऑपरेंड के संचालन को कार्यान्वित नहीं करते हैं तो संख्यात्मक विधियां और समृद्ध तुलना विधियां इस मान को लौटा सकती हैं। (दुभाषिया फिर ऑपरेटर के आधार पर परिलक्षित ऑपरेशन, या कुछ अन्य कमियां की कोशिश करेगा।) इसका सत्य मूल्य सत्य है।
इस मामले में ऑपरेटर दूसरे ऑपरेंड की परिलक्षित विधि के लिए तुलना ऑपरेशन को दर्शाता है । प्रलेखन परिभाषित करता है के रूप में परिलक्षित तरीके:
इन तरीकों का कोई स्वैप-तर्क संस्करण नहीं है (जब बाएं तर्क ऑपरेशन का समर्थन नहीं करता है लेकिन सही तर्क करता है); बल्कि, __lt__()
और __gt__()
एक-दूसरे के प्रतिबिंब हैं, __le__()
और __ge__()
एक-दूसरे के प्रतिबिंब हैं, और
__eq__()
और __ne__()
अपने स्वयं के प्रतिबिंब हैं।
परिणाम इस तरह दिखता है:
def __eq__(self, other):
"""Overrides the default implementation"""
if isinstance(other, Number):
return self.number == other.number
return NotImplemented
def __ne__(self, other):
"""Overrides the default implementation (unnecessary in Python 3)"""
x = self.__eq__(other)
if x is NotImplemented:
return NotImplemented
return not x
रिटर्निंग NotImplemented
के बजाय मूल्य False
अगर नई शैली कक्षाओं के लिए भी ऐसा करना सही है commutativity की ==
और !=
ऑपरेटरों जब ऑपरेंड असंबंधित प्रकार (कोई विरासत) के हैं वांछित है।
क्या हम अब भी वहां हैं? काफी नहीं। हमारे पास कितने विशिष्ट नंबर हैं?
len(set([n1, n2, n3])) # 3 -- oops
सेट ऑब्जेक्ट्स के हैश का उपयोग करते हैं, और डिफ़ॉल्ट रूप से पायथन ऑब्जेक्ट के पहचानकर्ता का हैश लौटाता है। आइए इसे ओवरराइड करने का प्रयास करें:
def __hash__(self):
"""Overrides the default implementation"""
return hash(tuple(sorted(self.__dict__.items())))
len(set([n1, n2, n3])) # 1
अंतिम परिणाम इस तरह दिखता है (मैंने सत्यापन के लिए अंत में कुछ दावे जोड़े):
class Number:
def __init__(self, number):
self.number = number
def __eq__(self, other):
"""Overrides the default implementation"""
if isinstance(other, Number):
return self.number == other.number
return NotImplemented
def __ne__(self, other):
"""Overrides the default implementation (unnecessary in Python 3)"""
x = self.__eq__(other)
if x is not NotImplemented:
return not x
return NotImplemented
def __hash__(self):
"""Overrides the default implementation"""
return hash(tuple(sorted(self.__dict__.items())))
class SubNumber(Number):
pass
n1 = Number(1)
n2 = Number(1)
n3 = SubNumber(1)
n4 = SubNumber(4)
assert n1 == n2
assert n2 == n1
assert not n1 != n2
assert not n2 != n1
assert n1 == n3
assert n3 == n1
assert not n1 != n3
assert not n3 != n1
assert not n1 == n4
assert not n4 == n1
assert n1 != n4
assert n4 != n1
assert len(set([n1, n2, n3, ])) == 1
assert len(set([n1, n2, n3, n4])) == 2
is
मूल्य तुलना से वस्तु पहचान को अलग करने के लिए ऑपरेटर है।