__lt__ के बजाय __cmp__


100

पायथन 2.x के पास तुलना ऑपरेटरों को ओवरलोड करने के दो तरीके हैं, __cmp__या "समृद्ध तुलना ऑपरेटर" जैसे __lt__समृद्ध तुलना अधिभार को प्राथमिकता दी जाती है, लेकिन ऐसा क्यों है?

अमीर तुलना ऑपरेटर प्रत्येक को लागू करने के लिए सरल हैं, लेकिन आपको उनमें से कई को लगभग समान तर्क के साथ लागू करना होगा। हालांकि, यदि आप बिलिन cmpऔर टपल ऑर्डर का उपयोग कर सकते हैं , तो __cmp__काफी सरल हो जाता है और सभी तुलनाओं को पूरा करता है:

class A(object):
  def __init__(self, name, age, other):
    self.name = name
    self.age = age
    self.other = other
  def __cmp__(self, other):
    assert isinstance(other, A) # assumption for this example
    return cmp((self.name, self.age, self.other),
               (other.name, other.age, other.other))

यह सादगी समृद्ध तुलनाओं के सभी 6 (!) को ओवरलोड करने की तुलना में मेरी जरूरतों को पूरा करती प्रतीत होती है। (हालांकि, आप इसे "सिर्फ" 4 तक ले जा सकते हैं यदि आप "स्वैप किए गए तर्क" / परिलक्षित व्यवहार पर भरोसा करते हैं, लेकिन इसका परिणाम मेरी विनम्र राय में जटिलता की शुद्ध वृद्धि है।)

क्या कोई अप्रत्याशित नुकसान है जिससे मुझे अवगत कराने की आवश्यकता है यदि मैं केवल ओवरलोड करता हूं __cmp__ ?

मैं समझता हूँ <, <=,== , आदि ऑपरेटरों अन्य प्रयोजनों के लिए अतिभारित किया जा सकता है और उसे किसी भी वस्तु वे लौट सकते हैं। मैं उस दृष्टिकोण के गुणों के बारे में नहीं पूछ रहा हूं, लेकिन केवल इन अंतरों की तुलना करने के लिए इन संचालकों का उपयोग उसी अर्थ में करता हूं जब वे संख्याओं के लिए मायने रखते हैं।

अपडेट: जैसा कि क्रिस्टोफर ने बताया , cmp3.x में गायब हो रहा है। क्या ऐसे कोई विकल्प हैं जो उपर्युक्त की तुलना में कार्यान्वयन को आसान बनाते हैं __cmp__?


5
अपने आखिरी सवाल फिर से मेरा उत्तर देखें, लेकिन वास्तव में वहाँ एक डिजाइन कि चीजों को और भी तुम्हारा सहित कई कक्षाओं के लिए आसान बनाने के हैं (अभी आप एक mixin, metaclass, या वर्ग इसे लागू करने के लिए डेकोरेटर की जरूरत है) है: यदि एक कुंजी विशेष विधि मौजूद है, इसे मानों का एक टपल वापस करना चाहिए, और सभी तुलनित्र और हैश को उस टपल के संदर्भ में परिभाषित किया गया है। जब मैंने उसे समझाया, तो गुइडो को मेरा विचार पसंद आया, लेकिन फिर मैं अन्य चीजों में व्यस्त हो गया और कभी भी पीईपी लिखने के लिए आसपास नहीं गया ... शायद 3.2; ;-) के लिए। इस बीच मैं उसके लिए अपने मिश्रण का उपयोग करता रहता हूं! -)
एलेक्स मार्टेली

जवाबों:


90

हां, __lt__मिक्सिन क्लास (या मेटाक्लास, या क्लास डेकोरेटर के साथ सब कुछ लागू करना आसान है अगर आपका स्वाद इस तरह से चलता है)।

उदाहरण के लिए:

class ComparableMixin:
  def __eq__(self, other):
    return not self<other and not other<self
  def __ne__(self, other):
    return self<other or other<self
  def __gt__(self, other):
    return other<self
  def __ge__(self, other):
    return not self<other
  def __le__(self, other):
    return not other<self

अब आपकी कक्षा केवल परिभाषित कर सकती है __lt__और तुलनात्मकमिक्सिन से विरासत में गुणा कर सकती है (इसके बाद जो भी आधार आवश्यक हो, यदि कोई हो तो)। एक वर्ग डेकोरेटर काफी समान होगा, बस इसी तरह के कार्यों को सम्मिलित करने के रूप में नए वर्ग की विशेषताओं को सजाने के लिए (परिणाम सूक्ष्म रूप से तेजी से हो सकता है, स्मृति के संदर्भ में समान रूप से मिनट की लागत पर)।

बेशक, यदि आपकी कक्षा को लागू करने के लिए कुछ विशेष रूप से तेज़ तरीका है (उदाहरण के लिए) __eq__और __ne__, उन्हें सीधे परिभाषित करना चाहिए ताकि मिक्सर के संस्करण उपयोग न करें (उदाहरण के लिए, यह मामला है dict) - वास्तव में __ne__सुविधा के लिए परिभाषित किया जा सकता है ऐसा वह:

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

लेकिन ऊपर दिए गए कोड में मैं केवल का उपयोग कर मनभावन समरूपता रखना चाहता था <;-)। जैसा कि __cmp__जाना था, क्योंकि हम किया है __lt__और दोस्तों, क्यों एक और वास्तव में चारों ओर एक ही बात करने के लिए अलग तरह से रखने के लिए,? यह हर पाइथन रनटाइम (क्लासिक, जाइथन, आयरनपिथॉन, पाइपी, ...) में बस इतना डेड-वेट है। कोड जो निश्चित रूप से बग नहीं होगा वह कोड है जो वहां नहीं है - पायथन का सिद्धांत जिसे कार्य करने के लिए आदर्श रूप से एक होना चाहिए, (C में "C की आत्मा" खंड में समान सिद्धांत है आईएसओ मानक, btw)।

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

आगे संपादित करें: कई वर्गों के लिए तुलना और हैशिंग प्रदान करने के लिए वास्तव में एक और भी बेहतर तरीका है, जिसमें प्रश्न में एक __key__विधि भी शामिल है, जैसा कि मैंने अपनी टिप्पणी पर उल्लेख किया है। चूँकि मुझे इसके लिए पीईपी लिखने के लिए कभी भी आस-पास नहीं मिला, आप इसे पसंद करने पर वर्तमान में इसे मिक्सिन (& c) के साथ लागू कर सकते हैं:

class KeyedMixin:
  def __lt__(self, other):
    return self.__key__() < other.__key__()
  # and so on for other comparators, as above, plus:
  def __hash__(self):
    return hash(self.__key__())

यह उदाहरण के लिए अन्य उदाहरणों के साथ तुलना करने के लिए एक बहुत ही सामान्य मामला है, कुछ क्षेत्रों के साथ प्रत्येक के लिए एक टपल की तुलना करने के लिए उबालने के लिए - और फिर, हैशिंग को बिल्कुल उसी आधार पर लागू किया जाना चाहिए। __key__विशेष विधि पतों कि सीधे की जरूरत है।


देरी के लिए क्षमा करें @ आर। पटे, मैंने फैसला किया कि चूंकि मुझे वैसे भी संपादित करना था, इसलिए मुझे जल्दी-जल्दी भागने के बजाय सबसे अधिक गहन उत्तर प्रदान करना चाहिए (और मैंने अपने पुराने महत्वपूर्ण विचार को सुझाने के लिए फिर से संपादित किया है जो मुझे कभी भी पीईपिंग के लिए नहीं मिला, साथ ही साथ कैसे। एक मिश्रण के साथ इसे लागू करने के लिए)।
एलेक्स मार्टेली 2

मैं वास्तव में उस महत्वपूर्ण विचार को पसंद करता हूं , इसका उपयोग करने जा रहा हूं और देखता हूं कि यह कैसा लगता है। (हालांकि आरक्षित नाम के बजाय cmp_key या _cmp_key नाम दिया गया है।)

TypeError: Cannot create a consistent method resolution order (MRO) for bases object, ComparableMixinजब मैं पायथन में यह कोशिश करता हूं 3. gist.github.com/2696496
एडम पार्किन

2
पायथन में 2.7 + / 3.2 + आप functools.total_orderingअपने स्वयं के निर्माण के बजाय उपयोग कर सकते हैं ComparableMixim। जैसा कि jmagnusson के उत्तर
दिन

4
पायथन 3 में <लागू करने के लिए उपयोग करना __eq__बहुत बुरा विचार है, क्योंकि TypeError: unorderable types
अंती हापाला

49

इस मामले को आसान बनाने के अजगर 2.7 + / 3.2 +, में एक वर्ग डेकोरेटर वहाँ functools.total_ordering , कि लागू करने के लिए क्या एलेक्स पता चलता है इस्तेमाल किया जा सकता। डॉक्स से उदाहरण:

@total_ordering
class Student:
    def __eq__(self, other):
        return ((self.lastname.lower(), self.firstname.lower()) ==
                (other.lastname.lower(), other.firstname.lower()))
    def __lt__(self, other):
        return ((self.lastname.lower(), self.firstname.lower()) <
                (other.lastname.lower(), other.firstname.lower()))

9
total_ordering__ne__हालांकि लागू नहीं होता है , इसलिए बाहर देखो!
फ्लिम

3
@ फ़ीलम, यह नहीं है, लेकिन __ne__। लेकिन ऐसा इसलिए __ne__है क्योंकि डिफ़ॉल्ट कार्यान्वयन है जो करने के लिए प्रतिनिधि है __eq__। इसलिए यहां बाहर देखने के लिए कुछ भी नहीं है।
Jan Hudec

कम से कम एक आदेश संचालन को परिभाषित करना चाहिए: <> <=> = .... eq की आवश्यकता नहीं है कुल आदेश के रूप में अगर! एक <b और b <a a = b
Xanlantos

9

यह PEP 207 - रिच कम्पेरिजन द्वारा कवर किया गया है

इसके अलावा, __cmp__अजगर 3.0 में चला जाता है। (ध्यान दें कि यह http://docs.python.org/3.0/reference/datamodel.html पर मौजूद नहीं है, लेकिन यह http://docs.python.org/2.7/reference/datamodel.html पर है )


पीईपी केवल इस बात से संबंधित है कि अमीर तुलना की आवश्यकता क्यों है, जिस तरह से NumPy उपयोगकर्ता एक अनुक्रम वापस करने के लिए A <B चाहते हैं।

मुझे एहसास नहीं था कि यह निश्चित रूप से दूर जा रहा है, यह मुझे दुखी करता है। (लेकिन यह इंगित करने के लिए धन्यवाद।)

PEP भी चर्चा करता है कि "क्यों" उन्हें पसंद किया जाता है। अनिवार्य रूप से यह दक्षता के लिए उबलता है: 1. कोई भी संचालन को लागू करने की आवश्यकता नहीं है जो आपकी वस्तु के लिए कोई मतलब नहीं है (जैसे कि अनलोड किए गए संग्रह।) 2. कुछ संग्रह में कुछ प्रकार की तुलनाओं पर बहुत ही कुशल संचालन होता है। यदि आप उन्हें परिभाषित करते हैं तो अमीर तुलना दुभाषिया का फायदा उठाते हैं।
क्रिस्टोफर

1
1, अगर वे समझ में नहीं आता है, तो cmp को लागू न करें । पुन: 2, दोनों विकल्प होने से आपको आवश्यकतानुसार अनुकूलन कर सकते हैं, जबकि अभी भी जल्दी से प्रोटोटाइप और परीक्षण कर रहे हैं। न ही कोई मुझे बताता है कि इसे क्यों हटाया गया है। (अनिवार्य रूप से यह मेरे लिए डेवलपर दक्षता को उबालता है।) क्या यह संभव है कि समृद्ध तुलना सीएमपी कमबैक के साथ कम कुशल हो ? इससे मुझे कोई मतलब नहीं होगा।

1
@R। Pate, जैसा कि मैं अपने उत्तर में समझाने की कोशिश करता हूं, सामान्यता में कोई वास्तविक नुकसान नहीं है (चूंकि एक मिक्सिन, डेकोरेटर, या मेटाक्लास, आप आसानी से बस के संदर्भ में सब कुछ परिभाषित कर सकते हैं, यदि आप चाहें तो) और सभी पायथन कार्यान्वयन के लिए चारों ओर ले जाने के लिए। निरर्थक कोड वापस करने के लिए हमेशा के cmp में - सिर्फ पायथन उपयोगकर्ताओं को दो समकक्ष तरीके से चीजों को व्यक्त करने के लिए - पायथन के दाने के खिलाफ 100% भाग जाएगा।
एलेक्स मार्टेली 1

2

(टिप्पणी में टिप्पणी लेने के लिए संपादित 6/17/17।)

मैंने ऊपर दिए गए तुलनीय मिक्सिन उत्तर की कोशिश की। मैं "कोई नहीं" के साथ मुसीबत में चला गया। यहां एक संशोधित संस्करण है जो "कोई नहीं" के साथ समानता तुलना को संभालता है। (मैंने देखा कि असमानता की तुलना में किसी में भी कमी के कारण परेशान होने का कोई कारण नहीं है):


class ComparableMixin(object):

    def __eq__(self, other):
        if not isinstance(other, type(self)): 
            return NotImplemented
        else:
            return not self<other and not other<self

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

    def __gt__(self, other):
        if not isinstance(other, type(self)): 
            return NotImplemented
        else:
            return other<self

    def __ge__(self, other):
        if not isinstance(other, type(self)): 
            return NotImplemented
        else:
            return not self<other

    def __le__(self, other):
        if not isinstance(other, type(self)): 
            return NotImplemented
        else:
            return not other<self    

आपको क्या लगता है कि है selfसिंगलटन हो सकता है Noneकी NoneTypeऔर एक ही समय में अपने को लागू ComparableMixin? और वास्तव में यह नुस्खा पायथन 3 के लिए बुरा है।
एंटि हापाला

3
selfहोगा कभी नहीं हो None, ताकि शाखा पूरी तरह से जा सकते हैं। उपयोग न करें type(other) == type(None); बस उपयोग करें other is None। विशेष आवरण के बजाय None, परीक्षण यदि अन्य प्रकार के प्रकार का एक उदाहरण है self, और NotImplementedएकल नहीं है तो वापस लौटें : नहीं if not isinstance(other, type(self)): return NotImplemented। सभी तरीकों के लिए ऐसा करें। पाइथन इसके बाद दूसरे ऑपरेंड को जवाब देने का मौका दे सकता है।
मार्टिन पीटर्स

1

एलेक्स मार्टेली के से प्रेरित होकर ComparableMixinऔर KeyedMixinजवाब, मैं निम्नलिखित mixin के साथ आया था। यह आपको एक एकल _compare_to()विधि को लागू करने की अनुमति देता है , जो कुंजी-आधारित तुलनाओं का उपयोग करता है KeyedMixin, लेकिन आपके वर्ग के प्रकार के आधार पर सबसे कुशल तुलना कुंजी चुनने की अनुमति देता है other। (ध्यान दें कि यह मिश्रण उन वस्तुओं के लिए ज्यादा मदद नहीं करता है जिन्हें समानता के लिए परीक्षण किया जा सकता है लेकिन आदेश नहीं)।

class ComparableMixin(object):
    """mixin which implements rich comparison operators in terms of a single _compare_to() helper"""

    def _compare_to(self, other):
        """return keys to compare self to other.

        if self and other are comparable, this function 
        should return ``(self key, other key)``.
        if they aren't, it should return ``None`` instead.
        """
        raise NotImplementedError("_compare_to() must be implemented by subclass")

    def __eq__(self, other):
        keys = self._compare_to(other)
        return keys[0] == keys[1] if keys else NotImplemented

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

    def __lt__(self, other):
        keys = self._compare_to(other)
        return keys[0] < keys[1] if keys else NotImplemented

    def __le__(self, other):
        keys = self._compare_to(other)
        return keys[0] <= keys[1] if keys else NotImplemented

    def __gt__(self, other):
        keys = self._compare_to(other)
        return keys[0] > keys[1] if keys else NotImplemented

    def __ge__(self, other):
        keys = self._compare_to(other)
        return keys[0] >= keys[1] if keys else NotImplemented
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.