मैं इस प्रश्न के लिए पायथन 3 के लिए एक अद्यतन उत्तर लिख रहा हूं।
__eq__
पायथन में कैसे और किस क्रम में संभाला जाता है?
a == b
यह आमतौर पर समझा जाता है, लेकिन हमेशा ऐसा नहीं होता है, जो a == b
आक्रमण करता है a.__eq__(b)
, या type(a).__eq__(a, b)
।
स्पष्ट रूप से, मूल्यांकन का क्रम है:
- यदि
b
प्रकार एक सख्त उपवर्ग है (उसी प्रकार का नहीं) का a
प्रकार है और एक है __eq__
, तो इसे कॉल करें और यदि मूल्य लागू किया जाता है, तो तुलना वापस करें।
- और, यदि
a
है __eq__
, तो इसे कॉल करें और यदि तुलना की गई है, तो इसे वापस कर दें।
- इसके अलावा, देखें कि क्या हमने बी को कॉल नहीं किया है
__eq__
और यह उसके पास है, तो कॉल करें और अगर तुलना की जाती है तो उसे वापस करें,
- अंत में, पहचान के लिए तुलना, के रूप में एक ही तुलना करें
is
।
हमें पता है कि अगर विधि वापस आती है तो तुलना लागू नहीं होती है NotImplemented
।
(अजगर 2 में, एक था __cmp__
विधि थी जिसकी तलाश की गई थी, लेकिन इसे हटा दिया गया और अजगर 3 में हटा दिया गया)
आइए बी उपवर्ग ए देकर अपने लिए पहले चेक के व्यवहार का परीक्षण करें, जो दर्शाता है कि इस गिनती पर स्वीकृत उत्तर गलत है:
class A:
value = 3
def __eq__(self, other):
print('A __eq__ called')
return self.value == other.value
class B(A):
value = 4
def __eq__(self, other):
print('B __eq__ called')
return self.value == other.value
a, b = A(), B()
a == b
जो केवल B __eq__ called
लौटने से पहले प्रिंट करता है False
।
हम इस पूर्ण एल्गोरिथ्म को कैसे जानते हैं?
यहां अन्य उत्तर अपूर्ण और पुराने हैं, इसलिए मैं जानकारी को अपडेट करने जा रहा हूं और आपको दिखाता हूं कि आप अपने लिए यह कैसे देख सकते हैं।
यह सी स्तर पर संभाला जाता है।
हमें यहां कोड के दो अलग-अलग बिट्स देखने की जरूरत है - __eq__
कक्षा की वस्तुओं के लिए डिफ़ॉल्ट object
, और कोड जो दिखता है और कॉल __eq__
करता है चाहे वह डिफ़ॉल्ट __eq__
या कस्टम का उपयोग करता हो ।
चूक __eq__
खोज रहे हैं __eq__
में प्रासंगिक सी एपीआई डॉक्स से पता चलता है हमें कि __eq__
द्वारा नियंत्रित किया जाता tp_richcompare
है, जिसमें - "object"
प्रकार परिभाषा cpython/Objects/typeobject.c
में परिभाषित किया गया है object_richcompare
के लिए case Py_EQ:
।
case Py_EQ:
/* Return NotImplemented instead of False, so if two
objects are compared, both get a chance at the
comparison. See issue #1393. */
res = (self == other) ? Py_True : Py_NotImplemented;
Py_INCREF(res);
break;
इसलिए यहाँ, यदि self == other
हम लौटते हैं True
, तो हम NotImplemented
वस्तु वापस करते हैं । यह ऑब्जेक्ट के किसी भी उपवर्ग के लिए डिफ़ॉल्ट व्यवहार है जो अपनी स्वयं की __eq__
विधि को लागू नहीं करता है ।
कैसे __eq__
कहा जाता है
फिर हम C API डॉक्स, PyObject_RichCompare फ़ंक्शन ढूंढते हैं, जो कॉल करता है do_richcompare
।
फिर हम देखते हैं कि C परिभाषा tp_richcompare
के लिए बनाए गए फ़ंक्शन "object"
को कहा जाता है do_richcompare
, तो आइए इसे थोड़ा और करीब से देखें।
इस फ़ंक्शन में पहली जाँच उन स्थितियों के लिए है जिनकी तुलना की जा रही है:
- हैं नहींएक ही प्रकार के , लेकिन
- दूसरा प्रकार पहले प्रकार का एक उपवर्ग है, और
- दूसरे प्रकार के एक
__eq__
विधि है,
इसके बाद दूसरे की विधि को स्वैप किए गए तर्कों के साथ कॉल करें, यदि मूल्य लागू हो तो वापस कर दें। यदि वह विधि लागू नहीं हुई है, तो हम जारी रखते हैं ...
if (!Py_IS_TYPE(v, Py_TYPE(w)) &&
PyType_IsSubtype(Py_TYPE(w), Py_TYPE(v)) &&
(f = Py_TYPE(w)->tp_richcompare) != NULL) {
checked_reverse_op = 1;
res = (*f)(w, v, _Py_SwappedOp[op]);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
आगे हम देखते हैं कि क्या हम __eq__
पहले प्रकार से विधि खोज सकते हैं और इसे कॉल कर सकते हैं। जब तक परिणाम NotImplemented नहीं होता है, अर्थात इसे लागू किया जाता है, हम इसे वापस करते हैं।
if ((f = Py_TYPE(v)->tp_richcompare) != NULL) {
res = (*f)(v, w, op);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
और यदि हमने दूसरे प्रकार की विधि की कोशिश नहीं की और यह वहां है, तो हम इसे आजमाते हैं, और यदि तुलना को लागू किया जाता है, तो हम इसे वापस कर देते हैं।
if (!checked_reverse_op && (f = Py_TYPE(w)->tp_richcompare) != NULL) {
res = (*f)(w, v, _Py_SwappedOp[op]);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
}
अंत में, हम उस स्थिति में आते हैं जब इसे किसी एक के प्रकार के लिए लागू नहीं किया जाता है।
ऑब्जेक्ट की पहचान के लिए फ़ॉलबैक जाँचता है, कि क्या यह मेमोरी में एक ही जगह पर एक ही ऑब्जेक्ट है - यह उसी तरह की जाँच है जैसे self is other
:
/* If neither object implements it, provide a sensible default
for == and !=, but raise an exception for ordering. */
switch (op) {
case Py_EQ:
res = (v == w) ? Py_True : Py_False;
break;
निष्कर्ष
एक तुलना में, हम पहले तुलना के उपवर्ग कार्यान्वयन का सम्मान करते हैं।
फिर हम पहली वस्तु के कार्यान्वयन के साथ तुलना करने का प्रयास करते हैं, फिर दूसरे के साथ अगर इसे नहीं बुलाया गया।
अंत में हम समानता के लिए तुलना के लिए पहचान के लिए एक परीक्षण का उपयोग करते हैं।