अजगर, क्या मुझे __e__ पर आधारित __ne __ () ऑपरेटर को लागू करना चाहिए?


98

मेरे पास एक वर्ग है जहां मैं __eq__()ऑपरेटर को ओवरराइड करना चाहता हूं । यह समझ में आता है कि मुझे __ne__()ऑपरेटर को भी ओवरराइड करना चाहिए , लेकिन क्या यह इस तरह के __ne__आधार पर लागू करने के लिए समझ में आता है __eq__?

class A:
    def __eq__(self, other):
        return self.value == other.value

    def __ne__(self, other):
        return not self.__eq__(other)

या क्या कुछ ऐसा है जो मुझे याद है कि पायथन इन ऑपरेटरों का उपयोग करता है जो इसे एक अच्छा विचार नहीं बनाता है?

जवाबों:


57

हां, यह पूरी तरह से ठीक है। वास्तव में, जब आप परिभाषित करते हैं तो प्रलेखन आपको परिभाषित करने का आग्रह __ne__करता है __eq__:

तुलना ऑपरेटरों के बीच कोई निहित संबंध नहीं हैं। सत्य का x==yअर्थ x!=y असत्य नहीं है । तदनुसार, परिभाषित करते समय __eq__(), एक को भी परिभाषित करना चाहिए __ne__()ताकि ऑपरेटर अपेक्षा के अनुरूप व्यवहार करेंगे।

बहुत सारे मामलों में (जैसे यह एक), यह उतना ही सरल होगा जितना कि इसके परिणाम को नकारना __eq__, लेकिन हमेशा नहीं।


12
यह सही उत्तर है (नीचे यहाँ, @ aaron-hall द्वारा)। आपके द्वारा उद्धृत दस्तावेज़ आपको उपयोग करने को लागू करने के लिए प्रोत्साहित नहीं करता है , केवल यह कि आप इसे लागू करते हैं। __ne____eq__
मसराद

2
@guyarad: वास्तव में, हारून का जवाब अभी भी थोड़ा गलत है, ठीक से प्रतिनिधि नहीं करने के लिए धन्यवाद; NotImplementedएक तरफ से वापसी के इलाज के बजाय __ne__दूसरी तरफ प्रतिनिधि के लिए एक संकेत के रूप में है , not self == other(यह मानते हुए कि ऑपरेंड __eq__को दूसरे ऑपरेंड की तुलना कैसे करनी है) का अर्थ __eq__है कि दूसरी तरफ से प्रतिनिधि बनाना । अजीब प्रकार के लिए, जैसे SQLAlchemy ORM के क्षेत्र, यह समस्याओं का कारण बनता है
शैडो रेंजर

1
ShadowRanger की आलोचना केवल बहुत ही रोग संबंधी मामलों (IMHO) पर लागू होगी और नीचे दिए गए मेरे उत्तर में पूरी तरह से संबोधित है।
हारून हॉल

1
नए दस्तावेज़ (3.7 के लिए कम से कम, पहले भी हो सकते हैं) __ne__स्वचालित रूप से प्रतिनिधि को सौंपते हैं __eq__और इस उत्तर में बोली अब डॉक्स में मौजूद नहीं है। लब्बोलुआब यह केवल लागू करने __eq__और __ne__प्रतिनिधि को जाने देने के लिए पूरी तरह से अजगर है ।
ब्लूसमर्स

132

अजगर, क्या मुझे __ne__()ऑपरेटर के आधार पर लागू करना चाहिए __eq__?

संक्षिप्त उत्तर: इसे लागू न करें, लेकिन यदि आपको इसका उपयोग करना है, तो ==नहीं__eq__

पायथन 3 में, डिफ़ॉल्ट रूप !=से निषेध है ==, इसलिए आपको लिखने के लिए भी आवश्यक नहीं है __ne__, और दस्तावेज़ को अब एक लिखने पर राय नहीं दी गई है।

आम तौर पर, पायथन 3-केवल कोड के लिए, एक को तब तक न लिखें जब तक आपको मूल कार्यान्वयन के लिए ओवरडोज़ करने की आवश्यकता न हो, जैसे कि एक बेसिन ऑब्जेक्ट के लिए।

यही है, रेमंड हेटिंगर की टिप्पणी को ध्यान में रखें :

यदि कोई सुपरक्लॉस में पहले से ही परिभाषित नहीं है, तो यह __ne__विधि स्वचालित रूप से __eq__केवल इस प्रकार __ne__है। इसलिए, यदि आप एक बिलिन से विरासत में मिले हैं, तो दोनों को ओवरराइड करना सबसे अच्छा है।

यदि आपको Python 2 में काम करने के लिए अपने कोड की आवश्यकता है, तो Python 2 की अनुशंसा का पालन करें और यह Python 3 में ठीक काम करेगा।

पायथन 2 में, पायथन खुद ही किसी अन्य के संदर्भ में किसी भी ऑपरेशन को स्वचालित रूप से लागू नहीं करता है - इसलिए, आपको इसके बजाय __ne__में परिभाषित करना चाहिए । ईजी==__eq__

class A(object):
    def __eq__(self, other):
        return self.value == other.value

    def __ne__(self, other):
        return not self == other # NOT `return not self.__eq__(other)`

सबूत देखिए कि

  • लागू करने वाला __ne__()ऑपरेटर __eq__और उसके आधार पर
  • __ne__पायथन 2 में बिल्कुल भी लागू नहीं होना

नीचे प्रदर्शन में गलत व्यवहार प्रदान करता है।

लंबा जवाब

प्रलेखन अजगर 2 के लिए कहते हैं:

तुलना ऑपरेटरों के बीच कोई निहित संबंध नहीं हैं। सत्य का x==yअर्थ x!=yअसत्य नहीं है । तदनुसार, परिभाषित करते समय __eq__(), एक को भी परिभाषित करना चाहिए __ne__()ताकि ऑपरेटर अपेक्षा के अनुरूप व्यवहार करेंगे।

तो इसका मतलब है कि अगर हम __ne__उलटा के संदर्भ में परिभाषित करते हैं __eq__, तो हम लगातार व्यवहार प्राप्त कर सकते हैं।

प्रलेखन का यह खंड पायथन 3 के लिए अद्यतन किया गया है :

डिफ़ॉल्ट रूप से, जब तक यह नहीं होता है तब तक परिणाम को दर्शाता __ne__()है __eq__()और इनवर्ट करता है NotImplemented

और "नया क्या है" अनुभाग में , हम देखते हैं कि यह व्यवहार बदल गया है:

  • !=अब ==जब तक नहीं ==लौटाता है , के विपरीत है NotImplemented

लागू करने के लिए __ne__, हम==__eq__ सीधे विधि का उपयोग करने के बजाय ऑपरेटर का उपयोग करना पसंद करते हैं ताकि यदि self.__eq__(other)कोई उप- NotImplementedप्रकार की जाँच के लिए वापस आए , तो पायथन उचित रूप other.__eq__(self) से दस्तावेज़ से जाँच करेगा :

NotImplementedवस्तु

इस प्रकार का एकल मान है। इस मान के साथ एक एकल ऑब्जेक्ट है। यह ऑब्जेक्ट बिल्ट-इन नाम से एक्सेस किया गया है NotImplemented। यदि वे प्रदान किए गए ऑपरेंड के लिए संचालन को कार्यान्वित नहीं करते हैं तो संख्यात्मक विधियाँ और समृद्ध तुलना विधियाँ इस मान को लौटा सकती हैं। (दुभाषिया फिर ऑपरेटर के आधार पर परिलक्षित ऑपरेशन, या कुछ अन्य कमियां की कोशिश करेगा।) इसका सत्य मूल्य सत्य है।

जब एक अमीर तुलना ऑपरेटर दिया है, अगर वे एक ही प्रकार, नहीं कर रहे हैं अजगर जांच करता है कि otherएक उप-प्रकार है, और अगर यह है कि ऑपरेटर परिभाषित है, यह का उपयोग करता है otherके विधि पहले (उलटा के लिए <, <=, >=और >)। यदि NotImplementedलौटाया जाता है, तो यह विपरीत विधि का उपयोग करता है। (यह एक ही विधि के लिए दो बार जांच नहीं करता है ।) ==ऑपरेटर का उपयोग करने से इस तर्क के लिए जगह मिलती है।


उम्मीदें

शब्दार्थ, आपको __ne__समानता के लिए चेक के संदर्भ में लागू करना चाहिए क्योंकि आपकी कक्षा के उपयोगकर्ता ए के सभी उदाहरणों के लिए निम्नलिखित कार्यों की अपेक्षा करेंगे।

def negation_of_equals(inst1, inst2):
    """always should return same as not_equals(inst1, inst2)"""
    return not inst1 == inst2

def not_equals(inst1, inst2):
    """always should return same as negation_of_equals(inst1, inst2)"""
    return inst1 != inst2

यही है, उपरोक्त दोनों कार्यों को हमेशा एक ही परिणाम देना चाहिए । लेकिन यह प्रोग्रामर पर निर्भर है।

जब परिभाषित करने __ne__पर अप्रत्याशित व्यवहार का प्रदर्शन __eq__:

पहला सेटअप:

class BaseEquatable(object):
    def __init__(self, x):
        self.x = x
    def __eq__(self, other):
        return isinstance(other, BaseEquatable) and self.x == other.x

class ComparableWrong(BaseEquatable):
    def __ne__(self, other):
        return not self.__eq__(other)

class ComparableRight(BaseEquatable):
    def __ne__(self, other):
        return not self == other

class EqMixin(object):
    def __eq__(self, other):
        """override Base __eq__ & bounce to other for __eq__, e.g. 
        if issubclass(type(self), type(other)): # True in this example
        """
        return NotImplemented

class ChildComparableWrong(EqMixin, ComparableWrong):
    """__ne__ the wrong way (__eq__ directly)"""

class ChildComparableRight(EqMixin, ComparableRight):
    """__ne__ the right way (uses ==)"""

class ChildComparablePy3(EqMixin, BaseEquatable):
    """No __ne__, only right in Python 3."""

तत्काल गैर-समकक्ष उदाहरण:

right1, right2 = ComparableRight(1), ChildComparableRight(2)
wrong1, wrong2 = ComparableWrong(1), ChildComparableWrong(2)
right_py3_1, right_py3_2 = BaseEquatable(1), ChildComparablePy3(2)

अपेक्षित् व्यवहार:

(ध्यान दें: जबकि नीचे के प्रत्येक के प्रत्येक दूसरे दावे के बराबर है और इसलिए तार्किक रूप से इससे पहले कि यह अनावश्यक है, मैं उन्हें प्रदर्शित करने के लिए शामिल हूं कि जब कोई दूसरे का उपवर्ग हो तो यह कोई फर्क नहीं पड़ता। )

इन उदाहरणों के __ne__साथ लागू किया गया है ==:

assert not right1 == right2
assert not right2 == right1
assert right1 != right2
assert right2 != right1

ये उदाहरण, पायथन 3 के तहत परीक्षण, भी सही ढंग से काम करते हैं:

assert not right_py3_1 == right_py3_2
assert not right_py3_2 == right_py3_1
assert right_py3_1 != right_py3_2
assert right_py3_2 != right_py3_1

और याद रखें कि ये __ne__लागू हो चुके हैं __eq__- जबकि यह अपेक्षित व्यवहार है, कार्यान्वयन गलत है:

assert not wrong1 == wrong2         # These are contradicted by the
assert not wrong2 == wrong1         # below unexpected behavior!

अप्रत्याशित व्यवहार:

ध्यान दें कि यह तुलना ऊपर ( not wrong1 == wrong2) से तुलना करती है ।

>>> assert wrong1 != wrong2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

तथा,

>>> assert wrong2 != wrong1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

__ne__अजगर 2 में छोड़ें नहीं

सबूत के लिए कि आपको __ne__पायथन 2 में कार्यान्वयन को छोड़ना नहीं चाहिए , इन समकक्ष वस्तुओं को देखें:

>>> right_py3_1, right_py3_1child = BaseEquatable(1), ChildComparablePy3(1)
>>> right_py3_1 != right_py3_1child # as evaluated in Python 2!
True

उपरोक्त परिणाम होना चाहिए False!

पायथन 3 स्रोत

के लिए डिफ़ॉल्ट CPython कार्यान्वयन __ne__में है typeobject.cमेंobject_richcompare :

case Py_NE:
    /* By default, __ne__() delegates to __eq__() and inverts the result,
       unless the latter returns NotImplemented. */
    if (Py_TYPE(self)->tp_richcompare == NULL) {
        res = Py_NotImplemented;
        Py_INCREF(res);
        break;
    }
    res = (*Py_TYPE(self)->tp_richcompare)(self, other, Py_EQ);
    if (res != NULL && res != Py_NotImplemented) {
        int ok = PyObject_IsTrue(res);
        Py_DECREF(res);
        if (ok < 0)
            res = NULL;
        else {
            if (ok)
                res = Py_False;
            else
                res = Py_True;
            Py_INCREF(res);
        }
    }
    break;

लेकिन डिफ़ॉल्ट __ne__उपयोग करता है __eq__?

__ne__सी स्तर पर पायथन 3 का डिफ़ॉल्ट कार्यान्वयन विवरण __eq__उच्च स्तर ==( PyObject_RichCompare ) का उपयोग कम कुशल होगा - और इसलिए इसे भी संभालना होगा NotImplemented

यदि __eq__इसे सही तरीके से लागू किया जाता है, तो इसका निषेध ==भी सही है - और यह हमें अपने में निम्न स्तर के कार्यान्वयन विवरण से बचने की अनुमति देता है __ne__

उपयोग ==करने से हम अपने निम्न स्तर के तर्क को एक स्थान पर रख सकते हैं , और संबोधित करने से बच सकतेNotImplemented हैं __ne__

कोई गलत तरीके से मान सकता है कि ==वापस आ सकता है NotImplemented

यह वास्तव में डिफ़ॉल्ट कार्यान्वयन के रूप में एक ही तर्क का उपयोग करता है __eq__, जो पहचान के लिए जाँच करता है (नीचे do_richcompare और हमारे साक्ष्य देखें)

class Foo:
    def __ne__(self, other):
        return NotImplemented
    __eq__ = __ne__

f = Foo()
f2 = Foo()

और तुलना:

>>> f == f
True
>>> f != f
False
>>> f2 == f
False
>>> f2 != f
True

प्रदर्शन

इसके लिए मेरा शब्द न लें, आइए देखें कि अधिक प्रदर्शन करने वाला क्या है:

class CLevel:
    "Use default logic programmed in C"

class HighLevelPython:
    def __ne__(self, other):
        return not self == other

class LowLevelPython:
    def __ne__(self, other):
        equal = self.__eq__(other)
        if equal is NotImplemented:
            return NotImplemented
        return not equal

def c_level():
    cl = CLevel()
    return lambda: cl != cl

def high_level_python():
    hlp = HighLevelPython()
    return lambda: hlp != hlp

def low_level_python():
    llp = LowLevelPython()
    return lambda: llp != llp

मुझे लगता है कि ये प्रदर्शन संख्याएं खुद के लिए बोलती हैं:

>>> import timeit
>>> min(timeit.repeat(c_level()))
0.09377292497083545
>>> min(timeit.repeat(high_level_python()))
0.2654011140111834
>>> min(timeit.repeat(low_level_python()))
0.3378178110579029

यह समझ में आता है जब आप समझते हैं कि low_level_pythonपायथन में तर्क है कि अन्यथा सी स्तर पर संभाला जाएगा।

कुछ आलोचकों का जवाब

एक अन्य उत्तरदाता लिखते हैं:

हारून हॉल के कार्यान्वयन not self == otherकी __ne__विधि सही नहीं है के रूप में यह कभी नहीं लौट सकते हैं NotImplemented( not NotImplementedहै False) और इसलिए __ne__विधि प्राथमिकता है कि पर वापस गिर कभी नहीं हो सकता __ne__विधि है कि प्राथमिकता नहीं है।

बीत रहा है __ne__कभी नहीं बदले NotImplementedयह गलत नहीं है। इसके बजाय, हम NotImplementedसमानता के लिए चेक के माध्यम से प्राथमिकता को संभालते हैं ==। मान लिया गया ==कि सही तरीके से लागू किया गया है, तो हम कर रहे हैं।

not self == otherडिफ़ॉल्ट पायथन 3 __ne__पद्धति का कार्यान्वयन हुआ करता था, लेकिन यह एक बग था और इसे जनवरी 2015 को पायथन 3.4 में सही कर दिया गया था, क्योंकि ShadowRanger पर ध्यान दिया गया था (# 21408 अंक देखें)।

खैर, यह समझाते हैं।

जैसा कि पहले उल्लेख किया गया है, __ne__अगर self.__eq__(other)रिटर्न NotImplemented(एक सिंगलटन) - पहले चेक द्वारा डिफ़ॉल्ट रूप से पायथन 3 - जिसके साथ जांच की जानी चाहिए isऔर यदि ऐसा है, तो वापस लौटना चाहिए। यहाँ उस तर्क को एक वर्ग मिश्रण के रूप में लिखा गया है:

class CStyle__ne__:
    """Mixin that provides __ne__ functionality equivalent to 
    the builtin functionality
    """
    def __ne__(self, other):
        equal = self.__eq__(other)
        if equal is NotImplemented:
            return NotImplemented
        return not equal

यह सी स्तर पायथन एपीआई के लिए शुद्धता के लिए आवश्यक है, और इसे पायथन 3 में पेश किया गया था

अनावश्यक। सभी संबंधित __ne__तरीकों को हटा दिया गया था, जिसमें स्वयं के चेक को लागू करने वाले भी शामिल थे जो __eq__सीधे या उसके माध्यम से प्रतिनिधि ==- और ==ऐसा करने का सबसे आम तरीका था।

समरूपता महत्वपूर्ण है?

हमारे लगातार आलोचक से निपटने के लिए मामला बनाने के लिए एक रोग उदाहरण प्रदान करता है NotImplementedमें __ne__, सभी के ऊपर समरूपता मूल्य। आइए स्टील-मैन को एक स्पष्ट उदाहरण के साथ तर्क दें:

class B:
    """
    this class has no __eq__ implementation, but asserts 
    any instance is not equal to any other object
    """
    def __ne__(self, other):
        return True

class A:
    "This class asserts instances are equivalent to all other objects"
    def __eq__(self, other):
        return True

>>> A() == B(), B() == A(), A() != B(), B() != A()
(True, True, False, True)

इसलिए, इस तर्क से, समरूपता बनाए रखने के लिए, हमें __ne__पाइथन संस्करण की परवाह किए बिना, जटिल लिखना होगा ।

class B:
    def __ne__(self, other):
        return True

class A:
    def __eq__(self, other):
        return True
    def __ne__(self, other):
        result = other.__eq__(self)
        if result is NotImplemented:
            return NotImplemented
        return not result

>>> A() == B(), B() == A(), A() != B(), B() != A()
(True, True, True, True)

स्पष्ट रूप से हमें इस बात पर कोई ध्यान नहीं देना चाहिए कि ये उदाहरण दोनों समान हैं और समान नहीं हैं।

मेरा प्रस्ताव है कि समरूपता समझदार कोड के अनुमान और प्रलेखन की सलाह का पालन करने से कम महत्वपूर्ण है।

हालाँकि, यदि A का एक समझदार क्रियान्वयन होता __eq__, तो हम अभी भी यहाँ अपनी दिशा का पालन कर सकते थे और हमारे पास अभी भी समरूपता होगी:

class B:
    def __ne__(self, other):
        return True

class A:
    def __eq__(self, other):
        return False         # <- this boolean changed... 

>>> A() == B(), B() == A(), A() != B(), B() != A()
(False, False, True, True)

निष्कर्ष

पायथन 2 संगत कोड के लिए, ==कार्यान्वित करने के लिए उपयोग करें __ne__। यह अधिक है:

  • सही बात
  • सरल
  • performant

केवल पायथन 3 में, सी स्तर पर निम्न-स्तर की उपेक्षा का उपयोग करें - यह और भी अधिक सरल और प्रदर्शनकारी है (हालांकि प्रोग्रामर यह निर्धारित करने के लिए ज़िम्मेदार है कि यह सही है )।

फिर से, उच्च स्तर के पायथन में निम्न-स्तरीय तर्क लिखें।


3
बहुत बढ़िया उदाहरण! आश्चर्य का हिस्सा यह है कि ऑपरेंड्स का क्रम उनके "राइट-साइड" प्रतिबिंबों के साथ कुछ जादू विधियों के विपरीत बिल्कुल भी मायने नहीं रखता है । उस हिस्से को फिर से पुनरावृत्त करने के लिए, जो मुझे याद था (और जिसने मुझे बहुत समय दिया था): उपवर्ग की समृद्ध तुलना विधि को पहले आज़माया जाता है, भले ही कोड में सुपरक्लास हो या ऑपरेटर के बाईं ओर उप-वर्ग। यही कारण है कि आपका a1 != c2लौटा False--- यह नहीं चला a1.__ne__, लेकिन c2.__ne__, जिसने मिक्सिन के __eq__ तरीके को नकार दिया । चूंकि NotImplementedसत्य है, not NotImplementedहै False
केविन जे। चेस

2
आपके हाल के अपडेट सफलतापूर्वक प्रदर्शन लाभ का प्रदर्शन करते हैं not (self == other), लेकिन कोई भी यह तर्क नहीं दे रहा है कि यह तेज़ नहीं है (ठीक है, प्यादे पर किसी भी अन्य विकल्प की तुलना में तेज़ है)। यह समस्या कुछ मामलों में गलत है; पाइथन स्वयं करता था not (self == other), लेकिन बदल गया क्योंकि यह मनमाने उपवर्गों की उपस्थिति में गलत था । गलत उत्तर के लिए सबसे तेज अभी भी गलत है
शैडो रेंजर

1
विशिष्ट उदाहरण वास्तव में महत्वहीन है। समस्या यह है कि, आपके कार्यान्वयन में, आपके __ne__प्रतिनिधियों का व्यवहार __eq__(यदि आवश्यक हो तो दोनों पक्षों का), लेकिन यह दोनों "हार" होने पर भी कभी__ne__ भी दूसरी तरफ वापस नहीं आता है __eq__। सही __ne__अपने आप को सौंपता है __eq__, लेकिन अगर वह वापस लौटता है NotImplemented, तो वह दूसरी तरफ जाने के __ne__बजाय दूसरी तरफ जाने के लिए वापस गिर जाता है __eq__(क्योंकि दूसरी तरफ के पास प्रतिनिधि को चुनने के लिए स्पष्ट रूप से ऑप्ट-एड नहीं हो सकता है __eq__, और आपको नहीं करना चाहिए इसके लिए वह निर्णय लेना)।
शैडो रेंजर

1
@AaronHall: आज इस पर फिर से विचार करते हुए, मुझे नहीं लगता कि आपका कार्यान्वयन उपवर्गों के लिए सामान्य रूप से समस्याग्रस्त है (इसे तोड़ने के लिए यह अत्यंत जटिल होगा, और माता-पिता के पूर्ण ज्ञान के लिए माना जाने वाला उपवर्ग इसे से बचने में सक्षम होना चाहिए। )। लेकिन मैंने अपने उत्तर में सिर्फ एक गैर-जटिल उदाहरण दिया। गैर-पैथोलॉजिकल मामला SQLAlchemy का ORM है, जहां न तो या तो लौटता है __eq__और न ही एक प्रॉक्सी ऑब्जेक्ट (जो "सत्य" होता है)। गलत तरीके से लागू करने का मतलब तुलना के लिए ऑर्डर मायने रखता है (आपको केवल एक ऑर्डर में एक प्रॉक्सी मिलती है)। __ne__TrueFalse__ne__
शैडो रेंजर

1
स्पष्ट होने के लिए, 99% (या शायद 99.999%) मामलों में, आपका समाधान ठीक है, और (जाहिर है) तेजी से। लेकिन चूंकि आपके पास उन मामलों पर नियंत्रण नहीं है जहां यह ठीक नहीं है, एक पुस्तकालय लेखक के रूप में जिसका कोड दूसरों द्वारा इस्तेमाल किया जा सकता है (पढ़ें: कुछ भी लेकिन सरल एक-स्क्रिप्ट और मॉड्यूल केवल व्यक्तिगत उपयोग के लिए), आपको करना होगा ऑपरेटर ओवरलोडिंग के लिए सामान्य अनुबंध का पालन करने के लिए सही कार्यान्वयन का उपयोग करें और जो भी अन्य कोड आपको मिल सकता है उसके साथ काम करें। सौभाग्य से, Py3 पर, इस मामले में से कोई भी नहीं, क्योंकि आप __ne__पूरी तरह से छोड़ सकते हैं । अब से एक साल बाद, Py2 मृत हो जाएगा और हम इसे अनदेखा करते हैं। :-)
शैडो रेंजर

10

बस रिकॉर्ड के लिए, एक कैनोनिकली सही और पार Py2 / Py3 पोर्टेबल __ne__जैसा दिखेगा:

import sys

class ...:
    ...
    def __eq__(self, other):
        ...

    if sys.version_info[0] == 2:
        def __ne__(self, other):
            equal = self.__eq__(other)
            return equal if equal is NotImplemented else not equal

यह __eq__आपके द्वारा परिभाषित किसी भी काम करता है :

  • इसके विपरीत not (self == other), तुलना करने वाले कुछ कष्टप्रद / जटिल मामलों में हस्तक्षेप नहीं करता है, जहां शामिल वर्गों में से एक का मतलब यह नहीं है कि परिणाम __ne__उसी के परिणामस्वरूप होता notहै __eq__(उदाहरण के लिए SQLAlchemy ORM, जहां दोनों __eq__और __ne__विशेष प्रॉक्सी ऑब्जेक्ट वापस आते हैं, नहीं Trueया False, और सही प्रॉक्सी ऑब्जेक्ट के बजाय, notके परिणाम पर __eq__लौटने की कोशिश कर रहा है False)।
  • इसके विपरीत not self.__eq__(other), यह सही रूप से __ne__दूसरे उदाहरण को दर्शाता है जब self.__eq__रिटर्न होता है NotImplemented( not self.__eq__(other)अतिरिक्त गलत होगा, क्योंकि NotImplementedसत्य है, इसलिए जब __eq__यह नहीं पता था कि तुलना कैसे करनी है, __ne__तो वापसी होगी False, यह कहते हुए कि दोनों वस्तुएं समान थीं जब वास्तव में केवल पूछे गए ऑब्जेक्ट का कोई अंदाजा नहीं था, जो कि डिफ़ॉल्ट रूप से नहीं के बराबर होगा)

यदि आपका रिटर्न का __eq__उपयोग नहीं करता NotImplementedहै, तो यह काम करता है (अर्थहीन ओवरहेड के साथ), यदि यह NotImplementedकभी-कभी उपयोग करता है, तो यह इसे ठीक से संभालता है। और पायथन संस्करण की जांच का अर्थ है कि यदि importपायथन 3 में वर्ग- ईड है __ne__, तो उसे अपरिभाषित छोड़ दिया जाता है, जिससे पायथन के मूल, कुशल कमबैक __ne__कार्यान्वयन (ऊपर का सी संस्करण) को लेने की अनुमति मिलती है ।


इसकी आवश्यकता क्यों है

पायथन ओवरलोडिंग नियम

आप अन्य समाधानों के बजाय ऐसा क्यों करते हैं इसका स्पष्टीकरण कुछ हद तक रहस्यमय है। पायथन में ओवरलोडिंग ऑपरेटरों और विशेष रूप से तुलना ऑपरेटरों के बारे में कुछ सामान्य नियम हैं:

  1. (सभी ऑपरेटरों पर लागू होता है) जब चल रहा हो LHS OP RHS, तो कोशिश करें LHS.__op__(RHS), और यदि वह वापस आए NotImplemented, तो प्रयास करें RHS.__rop__(LHS)। अपवाद: यदि RHSवर्ग का उपवर्ग है LHS, तो RHS.__rop__(LHS) पहले परीक्षण करें । तुलना ऑपरेटरों के मामले में, __eq__और __ne__अपने स्वयं के "आरपीपी" हैं (इसलिए के लिए परीक्षण का आदेश __ne__है LHS.__ne__(RHS), तो RHS.__ne__(LHS), उलटा है अगर 'वर्ग RHSका एक उपवर्ग है LHS)
  2. "स्वैप्ड" ऑपरेटर के विचार के अलावा, ऑपरेटरों के बीच कोई निहित संबंध नहीं है। यहां तक कि एक ही कक्षा के उदाहरण के लिए, LHS.__eq__(RHS)लौटने Trueसंकेत नहीं करता है LHS.__ne__(RHS)रिटर्न False(वास्तव में, ऑपरेटरों भी बूलियन मूल्यों वापस जाने के लिए आवश्यकता नहीं है; SQLAlchemy तरह ORMs जानबूझकर नहीं करते हैं, के लिए अनुमति एक अधिक अर्थपूर्ण क्वेरी सिंटैक्स)। पायथन 3 के रूप में, डिफ़ॉल्ट __ne__कार्यान्वयन इस तरह से व्यवहार करता है, लेकिन यह संविदात्मक नहीं है; आप उन __ne__तरीकों से ओवरराइड कर सकते हैं , जिनके सख्त विरोधी नहीं हैं __eq__

यह कैसे ओवरलोडिंग तुलनाकर्ताओं पर लागू होता है

इसलिए जब आप एक ऑपरेटर को ओवरलोड करते हैं, तो आपके पास दो काम होते हैं:

  1. यदि आप जानते हैं कि ऑपरेशन को स्वयं कैसे कार्यान्वित करना है, तो तुलना के रूप में केवल स्वयं के ज्ञान का उपयोग करते हुए ऐसा करें (कभी भी, प्रतिनिधि, स्पष्ट रूप से या स्पष्ट रूप से, ऑपरेशन के दूसरी तरफ, ताकि जोखिम गलत हो और / या अनंत पुनरावर्तन करें; आप इसे कैसे करते हैं इसके आधार पर)
  2. यदि आप नहीं जानते कि ऑपरेशन को स्वयं कैसे लागू किया जाए, तो हमेशा वापस लौटें NotImplemented, इसलिए पायथन दूसरे ऑपरेंड के कार्यान्वयन को सौंप सकता है

के साथ समस्या not self.__eq__(other)

def __ne__(self, other):
    return not self.__eq__(other)

कभी भी दूसरे पक्ष को नहीं सौंपता (और अगर __eq__सही तरीके से रिटर्न हो तो गलत है NotImplemented)। जब self.__eq__(other)रिटर्न NotImplemented(जो "सत्य" है), तो आप चुपचाप वापस लौटते हैं False, इसलिए A() != something_A_knows_nothing_aboutरिटर्न False, जब यह जाँचना चाहिए something_A_knows_nothing_aboutकि क्या उदाहरणों की तुलना करना जानता है A, और यदि ऐसा नहीं है, तो इसे वापस आ जाना चाहिए True(क्योंकि यदि कोई भी पक्ष नहीं जानता है तो कैसे लौटना चाहिए दूसरे की तुलना में, उन्हें एक दूसरे के बराबर नहीं माना जाता है)। यदि A.__eq__गलत तरीके से लागू किया गया है ( Falseइसके बजाय NotImplementedजब वह दूसरे पक्ष को नहीं पहचानता है) लौट रहा है , तो यह "सही" है Aपरिप्रेक्ष्य से, वापस लौटना True(क्योंकि Aयह समान नहीं है, इसलिए यह बराबर नहीं है), लेकिन यह हो सकता है से गलतsomething_A_knows_nothing_aboutपरिप्रेक्ष्य, क्योंकि यह भी कभी नहीं पूछा something_A_knows_nothing_about; A() != something_A_knows_nothing_aboutसमाप्त होता है True, लेकिन something_A_knows_nothing_about != A()कर सकते थे False, या किसी अन्य वापसी मान।

के साथ समस्या not self == other

def __ne__(self, other):
    return not self == other

अधिक सूक्ष्म है। यह 99% वर्गों के लिए सही होने जा रहा है, जिसमें सभी वर्ग शामिल हैं जिनके __ne__लिए तार्किक व्युत्क्रम है __eq__। लेकिन not self == otherटूटता दोनों नियम ऊपर उल्लेख किया, कक्षाओं के लिए जो साधन जहां __ne__ नहीं है की तार्किक उलटा __eq__, परिणाम, क्योंकि ऑपरेंड में से एक है, तो इसे लागू कर सकते हैं पूछा कभी नहीं है एक बार फिर गैर सममित हैं __ne__सब पर, यहां तक कि अन्य अगर ऑपरेंड नहीं कर सकता। सबसे सरल उदाहरण एक अजीब वर्ग है जो सभी तुलनाओं के Falseलिए लौटता है , इसलिए और दोनों वापस लौटते हैं । के एक सही कार्यान्वयन के साथ (एक जो लौटता है जब यह नहीं जानता कि तुलना कैसे की जाती है), संबंध सममित है; तथाA() == Incomparable()A() != Incomparable()FalseA.__ne__NotImplementedA() != Incomparable()Incomparable() != A()परिणाम पर सहमत (क्योंकि पूर्व मामले में, A.__ne__रिटर्न NotImplemented, फिर Incomparable.__ne__रिटर्न False, जबकि बाद में, सीधे Incomparable.__ne__रिटर्न False)। लेकिन जब A.__ne__इसे लागू किया जाता है return not self == other, तो A() != Incomparable()रिटर्न True(क्योंकि A.__eq__रिटर्न, नहीं NotImplemented, तो Incomparable.__eq__रिटर्न False, और A.__ne__इन्वर्ट करता है कि True), जबकि Incomparable() != A()रिटर्नFalse.

इसका एक उदाहरण आप यहां एक्शन में देख सकते हैं ।

जाहिर है, एक ऐसा वर्ग जो हमेशा Falseदोनों के लिए लौटता है __eq__और __ne__थोड़ा अजीब होता है। लेकिन जैसा कि पहले उल्लेख किया गया है, __eq__और __ne__लौटने की भी आवश्यकता नहीं है True/ False; SQLAlchemy ORM में तुलना करने वाले वर्ग होते हैं, जो क्वेरी बिल्डिंग के लिए एक विशेष प्रॉक्सी ऑब्जेक्ट लौटाते हैं, न कि True/ Falseबिल्कुल (वे "सत्य" हैं यदि एक बूलियन संदर्भ में मूल्यांकन किया जाता है, लेकिन उन्हें इस तरह के संदर्भ में मूल्यांकन नहीं करना चाहिए)।

__ne__ठीक से ओवरलोड करने में विफल रहने से, आप कोड के रूप में उस तरह की कक्षाओं को तोड़ देंगे :

 results = session.query(MyTable).filter(MyTable.fieldname != MyClassWithBadNE())

काम करेगा (SQLAlchemy यह जानता है कि MyClassWithBadNEएसक्यूएल स्ट्रिंग को कैसे सम्मिलित करना है? यह सभी प्रकार के एडेप्टर के साथ किया जा सकता है, जिसमें MyClassWithBadNEसहयोग नहीं किया जा सकता है ) filter, जबकि अपेक्षित प्रॉक्सी ऑब्जेक्ट को पास करते हुए:

 results = session.query(MyTable).filter(MyClassWithBadNE() != MyTable.fieldname)

filterएक सादे पासिंग को समाप्त करेगा False, क्योंकि self == otherएक प्रॉक्सी ऑब्जेक्ट लौटाता है, और not self == otherबस सत्य प्रॉक्सी ऑब्जेक्ट को परिवर्तित करता है False। उम्मीद है, filterजैसे अवैध तर्कों को नियंत्रित करने पर एक अपवाद फेंकता है False। हालांकि मुझे यकीन है कि कई लोग तर्क देंगे कि तुलना के बाएं हाथ पर लगातार MyTable.fieldname होना चाहिए , तथ्य यह है कि सामान्य मामले में इसे लागू करने के लिए कोई प्रोग्रामेटिक कारण नहीं है, और एक सही जेनेरिक __ne__किसी भी तरह से काम करेगा, जबकि return not self == otherकेवल काम करता है एक व्यवस्था में।


1
एकमात्र सही, पूर्ण और ईमानदार (क्षमा करें @AaronHall) उत्तर। यह स्वीकृत उत्तर होना चाहिए।
मैगीरो

4

संक्षिप्त उत्तर: हाँ (लेकिन इसे सही करने के लिए दस्तावेज़ पढ़ें)

ShadowRanger की __ne__विधि का कार्यान्वयन सही है (और यह __ne__पायथन 3.4 के बाद से विधि का डिफ़ॉल्ट कार्यान्वयन होता है ):

def __ne__(self, other):
    result = self.__eq__(other)

    if result is not NotImplemented:
        return not result

    return NotImplemented

क्यों? क्योंकि यह एक महत्वपूर्ण गणितीय संपत्ति, रहता समरूपता के !=ऑपरेटर। यह ऑपरेटर द्विआधारी है इसलिए इसका परिणाम दोनों ऑपरेंड के गतिशील प्रकार पर निर्भर होना चाहिए , न कि केवल एक पर। यह कई प्रेषण (जैसे जूलिया ) की अनुमति देने वाली प्रोग्रामिंग भाषाओं के लिए दोहरे प्रेषण के माध्यम से कार्यान्वित किया जाता है । पायथन में जो केवल एकल प्रेषण की अनुमति देता है, दोहरे प्रेषण को सांख्यिक विधियों के लिए सिम्युलेटेड किया जाता है और कार्यान्वयन विधियों में मान लौटाते हुए समृद्ध तुलना की जाती है जो अन्य ऑपरेंड के प्रकार का समर्थन नहीं करते हैं; दुभाषिया फिर दूसरे ऑपरेंड की परिलक्षित विधि का प्रयास करेगा।NotImplemented

हारून हॉल के कार्यान्वयन not self == otherकी __ne__विधि सही नहीं है के रूप में यह की समरूपता को हटा !=ऑपरेटर। वास्तव में, यह कभी नहीं लौट सकता NotImplemented( not NotImplementedहै False) और इसलिए __ne__उच्च प्राथमिकता वाली __ne__विधि कम प्राथमिकता के साथ विधि पर कभी वापस नहीं गिर सकती है। not self == otherइस __ne__विधि के डिफ़ॉल्ट पायथन 3 कार्यान्वयन के लिए उपयोग किया जाता है, लेकिन यह एक बग था जिसे जनवरी 2015 को पायथन 3.4 में सही किया गया था, जैसा कि ShadowRanger ने देखा ( # 21408 अंक देखें )।

तुलना ऑपरेटरों का कार्यान्वयन

अजगर भाषा संदर्भ पायथन के लिए अपने में 3 राज्यों अध्याय III डाटा मॉडल :

object.__lt__(self, other)
object.__le__(self, other)
object.__eq__(self, other)
object.__ne__(self, other)
object.__gt__(self, other)
object.__ge__(self, other)

ये तथाकथित "समृद्ध तुलना" विधियाँ हैं। ऑपरेटर प्रतीकों और विधि नामों के बीच पत्राचार इस प्रकार है: x<yकॉल x.__lt__(y), x<=yकॉल x.__le__(y), x==yकॉल x.__eq__(y), x!=yकॉल x.__ne__(y), x>yकॉल x.__gt__(y)और x>=y कॉल x.__ge__(y)

एक समृद्ध तुलना पद्धति सिंगलटन को लौटा सकती है NotImplementedयदि वह दिए गए तर्कों के लिए ऑपरेशन को लागू नहीं करता है।

इन विधियों का कोई स्वैप-तर्क संस्करण नहीं है (जब बाईं तर्क ऑपरेशन का समर्थन नहीं करता है लेकिन सही तर्क करता है); बल्कि, __lt__()और __gt__()एक-दूसरे के प्रतिबिंब हैं, __le__()और __ge__()एक-दूसरे के प्रतिबिंब हैं, और __eq__()और __ne__()अपने स्वयं के प्रतिबिंब हैं। यदि ऑपरेंड विभिन्न प्रकार के होते हैं, और राइट ऑपरेंड का प्रकार बाएं ऑपरेंड के प्रकार का प्रत्यक्ष या अप्रत्यक्ष उप-वर्ग होता है, तो सही ऑपरेंड के प्रतिबिंबित तरीके की प्राथमिकता होती है, अन्यथा बाएं ऑपरेंड के तरीके की प्राथमिकता होती है। वर्चुअल सबक्लासिंग पर विचार नहीं किया जाता है।

अजगर कोड में इस का अनुवाद (का उपयोग कर देता है operator_eqके लिए ==, operator_neके लिए !=, operator_ltके लिए <, operator_gtके लिए >, operator_leके लिए <=और operator_geके लिए >=):

def operator_eq(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__eq__(left)

        if result is NotImplemented:
            result = left.__eq__(right)
    else:
        result = left.__eq__(right)

        if result is NotImplemented:
            result = right.__eq__(left)

    if result is NotImplemented:
        result = left is right

    return result


def operator_ne(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__ne__(left)

        if result is NotImplemented:
            result = left.__ne__(right)
    else:
        result = left.__ne__(right)

        if result is NotImplemented:
            result = right.__ne__(left)

    if result is NotImplemented:
        result = left is not right

    return result


def operator_lt(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__gt__(left)

        if result is NotImplemented:
            result = left.__lt__(right)
    else:
        result = left.__lt__(right)

        if result is NotImplemented:
            result = right.__gt__(left)

    if result is NotImplemented:
        raise TypeError(f"'<' not supported between instances of '{type(left).__name__}' and '{type(right).__name__}'")

    return result


def operator_gt(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__lt__(left)

        if result is NotImplemented:
            result = left.__gt__(right)
    else:
        result = left.__gt__(right)

        if result is NotImplemented:
            result = right.__lt__(left)

    if result is NotImplemented:
        raise TypeError(f"'>' not supported between instances of '{type(left).__name__}' and '{type(right).__name__}'")

    return result


def operator_le(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__ge__(left)

        if result is NotImplemented:
            result = left.__le__(right)
    else:
        result = left.__le__(right)

        if result is NotImplemented:
            result = right.__ge__(left)

    if result is NotImplemented:
        raise TypeError(f"'<=' not supported between instances of '{type(left).__name__}' and '{type(right).__name__}'")

    return result


def operator_ge(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__le__(left)

        if result is NotImplemented:
            result = left.__ge__(right)
    else:
        result = left.__ge__(right)

        if result is NotImplemented:
            result = right.__le__(left)

    if result is NotImplemented:
        raise TypeError(f"'>=' not supported between instances of '{type(left).__name__}' and '{type(right).__name__}'")

    return result

तुलना विधियों का डिफ़ॉल्ट कार्यान्वयन

दस्तावेज़ कहते हैं:

डिफ़ॉल्ट रूप से, जब तक यह नहीं होता है तब तक परिणाम को दर्शाता __ne__()है __eq__()और इनवर्ट करता है NotImplemented। तुलना ऑपरेटरों के बीच कोई अन्य निहित संबंध नहीं हैं, उदाहरण के लिए, सच्चाई का (x<y or x==y)मतलब नहीं है x<=y

तुलना तरीकों के डिफ़ॉल्ट कार्यान्वयन ( __eq__, __ne__, __lt__, __gt__, __le__और __ge__) इस प्रकार से दिया जा सकता है:

def __eq__(self, other):
    return NotImplemented

def __ne__(self, other):
    result = self.__eq__(other)

    if result is not NotImplemented:
        return not result

    return NotImplemented

def __lt__(self, other):
    return NotImplemented

def __gt__(self, other):
    return NotImplemented

def __le__(self, other):
    return NotImplemented

def __ge__(self, other):
    return NotImplemented

तो यह __ne__विधि का सही कार्यान्वयन है । और यह हमेशा के प्रतिलोम वापस नहीं करता __eq__विधि क्योंकि जब __eq__विधि रिटर्न NotImplemented, इसके उलटा not NotImplementedहै False(के रूप में bool(NotImplemented)है Trueवांछित के बजाय) NotImplemented

का गलत क्रियान्वयन __ne__

जैसा कि आरोन हॉल ने ऊपर दिखाया है, not self.__eq__(other)यह __ne__विधि का डिफ़ॉल्ट कार्यान्वयन नहीं है । लेकिन न है not self == othernot self == otherदो मामलों में कार्यान्वयन के व्यवहार के साथ डिफ़ॉल्ट कार्यान्वयन के व्यवहार की तुलना करके उत्तरार्द्ध नीचे प्रदर्शित किया गया है :

  • __eq__विधि रिटर्न NotImplemented;
  • __eq__विधि से एक मूल्य के विभिन्न रिटर्न NotImplemented

डिफ़ॉल्ट कार्यान्वयन

आइए देखें कि क्या होता है जब A.__ne__विधि डिफ़ॉल्ट कार्यान्वयन का उपयोग करती है और A.__eq__विधि वापस आती है NotImplemented:

class A:
    pass


class B:

    def __ne__(self, other):
        return "B.__ne__"


assert (A() != B()) == "B.__ne__"
  1. !=कॉल करता है A.__ne__
  2. A.__ne__कॉल करता है A.__eq__
  3. A.__eq__लौटता है NotImplemented
  4. !=कॉल करता है B.__ne__
  5. B.__ne__लौटता है "B.__ne__"

इससे पता चलता है कि जब A.__eq__विधि वापस आती है NotImplemented, तो A.__ne__विधि विधि पर वापस आ जाती है B.__ne__

अब देखते हैं कि क्या होता है जब A.__ne__विधि डिफ़ॉल्ट कार्यान्वयन का उपयोग करती है और A.__eq__विधि एक मान को अलग से लौटाती है NotImplemented:

class A:

    def __eq__(self, other):
        return True


class B:

    def __ne__(self, other):
        return "B.__ne__"


assert (A() != B()) is False
  1. !=कॉल करता है A.__ne__
  2. A.__ne__कॉल करता है A.__eq__
  3. A.__eq__लौटता है True
  4. !=रिटर्न not True, कि है False

इससे पता चलता है कि इस मामले में, A.__ne__विधि विधि का व्युत्क्रम लौटाती है A.__eq__। इस प्रकार __ne__विधि प्रलेखन में विज्ञापित की तरह व्यवहार करती है।

A.__ne__ऊपर दिए गए सही कार्यान्वयन के साथ विधि के डिफ़ॉल्ट कार्यान्वयन को ओवरराइड करने से समान परिणाम मिलते हैं।

not self == other कार्यान्वयन

आइए देखते हैं कि कार्यान्वयन के A.__ne__साथ विधि के डिफ़ॉल्ट कार्यान्वयन को ओवरराइड करते समय क्या होता है not self == otherऔर A.__eq__विधि वापस आती है NotImplemented:

class A:

    def __ne__(self, other):
        return not self == other


class B:

    def __ne__(self, other):
        return "B.__ne__"


assert (A() != B()) is True
  1. !=कॉल करता है A.__ne__
  2. A.__ne__कॉल करता है ==
  3. ==कॉल करता है A.__eq__
  4. A.__eq__लौटता है NotImplemented
  5. ==कॉल करता है B.__eq__
  6. B.__eq__लौटता है NotImplemented
  7. ==रिटर्न A() is B(), कि है False
  8. A.__ne__रिटर्न not False, कि है True

__ne__विधि का डिफ़ॉल्ट कार्यान्वयन लौटा "B.__ne__", नहीं True

अब देखते हैं कि कार्यान्वयन के A.__ne__साथ विधि के डिफ़ॉल्ट कार्यान्वयन को ओवरराइड करते समय क्या होता है not self == otherऔर A.__eq__विधि एक मान को अलग से लौटाती है NotImplemented:

class A:

    def __eq__(self, other):
        return True

    def __ne__(self, other):
        return not self == other


class B:

    def __ne__(self, other):
        return "B.__ne__"


assert (A() != B()) is False
  1. !=कॉल करता है A.__ne__
  2. A.__ne__कॉल करता है ==
  3. ==कॉल करता है A.__eq__
  4. A.__eq__लौटता है True
  5. A.__ne__रिटर्न not True, कि है False

इस मामले में __ne__विधि का डिफ़ॉल्ट कार्यान्वयन भी वापस आ Falseगया।

चूंकि यह कार्यान्वयन __ne__विधि के डिफ़ॉल्ट कार्यान्वयन के व्यवहार को दोहराने में विफल रहता है , जब __eq__विधि वापस आती है NotImplemented, तो यह गलत है।


आपके अंतिम उदाहरण के लिए: "चूंकि यह कार्यान्वयन __ne__विधि के डिफ़ॉल्ट कार्यान्वयन के व्यवहार को दोहराने में विफल रहता है, जब __eq__विधि NotImplement की जाती है, तो यह गलत है।" - Aबिना शर्त समानता को परिभाषित करता है। इस प्रकार, A() == B()। इस प्रकार A() != B() मिथ्या होना चाहिए , और यह है । दिए गए उदाहरण पैथोलॉजिकल हैं (यानी __ne__एक स्ट्रिंग नहीं लौटना चाहिए, और __eq__इस पर निर्भर नहीं होना चाहिए __ne__- बल्कि __ne__इस पर निर्भर होना चाहिए __eq__, जो कि पायथन 3 में डिफ़ॉल्ट अपेक्षा है)। मैं इस उत्तर पर अभी भी -1 हूँ जब तक आप मेरा विचार नहीं बदल सकते।
हारून हॉल

@AaronHall पायथन भाषा संदर्भ से : "एक समृद्ध तुलना पद्धति एकलक को लौटा सकती है NotImplementedयदि यह किसी दिए गए तर्कों के लिए ऑपरेशन को लागू नहीं करता है। सम्मेलन द्वारा, Falseऔर Trueएक सफल तुलना के लिए लौटाया जाता है। हालांकि, ये विधियां किसी भी मूल्य को वापस कर सकती हैं। , इसलिए यदि तुलना ऑपरेटर का उपयोग बूलियन संदर्भ में किया जाता है (उदाहरण के लिए, यदि एक स्टेटमेंट की स्थिति में), तो पायथन bool()मूल्य निर्धारित करने के लिए कॉल करेगा कि क्या परिणाम सही है या गलत। "
मैग्गीरो

@AaronHall आपका कार्यान्वयन __ne__एक महत्वपूर्ण गणितीय संपत्ति, ऑपरेटर की समरूपता को मारता है !=। यह ऑपरेटर द्विआधारी है इसलिए इसका परिणाम केवल एक ही नहीं, दोनों ऑपरेंड के गतिशील प्रकार पर निर्भर होना चाहिए । यह सही ढंग से के माध्यम से भाषा की प्रोग्रामिंग में कार्यान्वित किया जाता है डबल प्रेषण की इजाजत दी भाषा के लिए कई प्रेषण । पायथन में जो केवल एकल प्रेषण की अनुमति देता है, NotImplementedमूल्य को वापस करके दोहरे प्रेषण का अनुकरण किया जाता है ।
मैगीरो

अंतिम उदाहरण में दो वर्ग हैं, Bजो सभी चेक के लिए एक सत्य स्ट्रिंग लौटाता है __ne__, और Aवह Trueसभी चेक पर रिटर्न देता है __eq__। यह एक पैथोलॉजिकल विरोधाभास है। इस तरह के विरोधाभास के तहत, एक अपवाद उठाना सबसे अच्छा होगा। के ज्ञान के बिना B, Aसम्मान करने के लिए बाध्य नहीं है Bकी के कार्यान्वयन __ne__समरूपता के प्रयोजनों के लिए। इस बिंदु पर उदाहरण में, कैसे Aकार्यान्वयन __ne__मेरे लिए अप्रासंगिक है। कृपया अपनी बात कहने के लिए एक व्यावहारिक, गैर-रोगात्मक मामला खोजें। मैंने आपको संबोधित करने के लिए अपना उत्तर अपडेट कर दिया है।
हारून हॉल

@AaronHall अधिक यथार्थवादी उदाहरण के लिए, SQLAlchemy उदाहरण @ShadowRanger द्वारा दिया गया देखें। यह भी ध्यान दें कि __ne__विशिष्ट उपयोग के मामलों में आपके कार्यान्वयन का तथ्य यह सही नहीं है। बोइंग 737 मैक्स विमानों ने दुर्घटनाओं से पहले 500,000 उड़ानें
भरीं

-1

के सभी हैं __eq__, __ne__, __lt__, __ge__, __le__, और __gt__वर्ग के लिए मेकअप भावना है, तो बस को लागू __cmp__करने के बजाय। अन्यथा, जैसा आप कर रहे हैं वैसा ही करें, क्योंकि बिट डैनियल डिपोलो ने कहा (जबकि मैं इसे देखने के बजाय इसका परीक्षण कर रहा था;);


12
__cmp__()विशेष विधि नहीं रह गया है अजगर 3.x में समर्थित है ताकि आपको शानदार तुलना ऑपरेटर्स का उपयोग आदत हो जाना चाहिए था।
डॉन ओ'डॉनेल 7

8
या वैकल्पिक रूप से यदि आप पाइथन 2.7 या 3.x में हैं, तो फंक्शनल टूल.टाल_ऑर्डरिंग डेकोरेटर काफी उपयोगी है।
एडम पार्किन

सर उठाने के लिए धन्यवाद। मैं पिछले साल और डेढ़ साल में उन रेखाओं के साथ कई चीजों को महसूस कर चुका हूं। ;)
कार्ल कूंचल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.