शब्दकोश कुंजी के रूप में कस्टम प्रकार की वस्तु


185

मुझे एक कस्टम प्रकार की अपनी वस्तुओं का उपयोग पायथन डिक्शनरी में कुंजियों के रूप में करने के लिए क्या करना चाहिए (जहां मुझे कुंजी के रूप में कार्य करने के लिए "ऑब्जेक्ट आईडी" नहीं चाहिए), जैसे

class MyThing:
    def __init__(self,name,location,length):
            self.name = name
            self.location = location
            self.length = length

मैं MyThing की कुंजियों का उपयोग करना चाहता हूं जिन्हें समान माना जाता है यदि नाम और स्थान समान हैं। C # / Java से मुझे ओवरराइड करने और एक समान और हैशकोड विधि प्रदान करने की आदत है, और कुछ भी हैशकोड को म्यूट न करने का वादा करता है।

इसे पूरा करने के लिए मुझे पायथन में क्या करना चाहिए? क्या मुझे भी करना चाहिए?

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


हैश का उपयोग करने में क्या गलत है?
राफे केटलर

5
शायद इसलिए कि वह दो चाहता है MyThing, यदि उनके पास समान है nameऔर location, उसी मूल्य को वापस करने के लिए शब्दकोश को अनुक्रमित करना है, भले ही वे अलग-अलग दो "ऑब्जेक्ट" के रूप में बनाए गए हों।
सांता

1
"शायद यह एक नाम (स्थान, नाम) को कुंजी के रूप में रखने के लिए बेहतर होगा - लेकिन विचार करें कि मैं एक वस्तु बनना चाहता हूं)" आपका मतलब है: एक गैर-समग्र वस्तु?
eyquem

जवाबों:


220

आपको 2 विधियाँ , नोट __hash__और जोड़ने की आवश्यकता है __eq__:

class MyThing:
    def __init__(self,name,location,length):
        self.name = name
        self.location = location
        self.length = length

    def __hash__(self):
        return hash((self.name, self.location))

    def __eq__(self, other):
        return (self.name, self.location) == (other.name, other.location)

    def __ne__(self, other):
        # Not strictly necessary, but to avoid having both x==y and x!=y
        # True at the same time
        return not(self == other)

पायथन तानाशाही दस्तावेज प्रमुख वस्तुओं पर इन आवश्यकताओं को परिभाषित करता है, अर्थात उन्हें धोने योग्य होना चाहिए ।


17
hash(self.name)की तुलना में अच्छा लग रहा है self.name.__hash__(), और यदि आप करते हैं और आप hash((x, y))XORing से बचने के लिए कर सकते हैं ।
रोश ऑक्सीमोरोन 19

5
एक अतिरिक्त नोट के रूप में, मैं सिर्फ पता चला कि बुला x.__hash__()भी है कि तरह गलत है, क्योंकि यह कर सकते हैं उत्पादन गलत परिणाम: pastebin.com/C9fSH7eF
रोश आक्सीमोरण

@ रोश ऑक्सीमोरन: टिप्पणी के लिए धन्यवाद। जब लेखन मैं स्पष्ट रूप से उपयोग कर रहा था and, __eq__लेकिन तब मैंने सोचा कि "ट्यूपल्स का उपयोग क्यों नहीं किया जा रहा है?" क्योंकि मैं अक्सर ऐसा करता हूं (मुझे लगता है कि यह अधिक पठनीय है)। __hash__हालांकि कुछ अजीब कारणों से मेरी आंखें इस बारे में सवाल नहीं करती थीं ।
६५०२

1
@ user877329: क्या आप कुंजियों के रूप में कुछ ब्लेंडर डेटा संरचना का उपयोग करने की कोशिश कर रहे हैं? कुछ रिपॉजिट से कुछ निश्चित वस्तुओं के लिए आपको उत्परिवर्तन से बचने के लिए पहले उन्हें "फ्रीज" करने की आवश्यकता होती है (एक मूल्य-आधारित वस्तु का उपयोग करना जो एक अजगर शब्दकोष में एक कुंजी के रूप में इस्तेमाल किया गया है) की अनुमति नहीं है
6502

1
@ kawing-चिऊ pythonfiddle.com/eq-method-needs-ne-method <- इस शो "बग" अजगर 2 में अजगर 3 इस समस्या नहीं है : डिफ़ॉल्ट रूप __ne__()दिया गया है "निर्धारित"
बॉब स्टीन

34

पाइथन 2.6 या इसके बाद के संस्करण में एक विकल्प का उपयोग करना है collections.namedtuple()- यह आपको किसी भी विशेष तरीके को लिखने से बचाता है:

from collections import namedtuple
MyThingBase = namedtuple("MyThingBase", ["name", "location"])
class MyThing(MyThingBase):
    def __new__(cls, name, location, length):
        obj = MyThingBase.__new__(cls, name, location)
        obj.length = length
        return obj

a = MyThing("a", "here", 10)
b = MyThing("a", "here", 20)
c = MyThing("c", "there", 10)
a == b
# True
hash(a) == hash(b)
# True
a == c
# False

20

आप ओवरराइड __hash__यदि आप विशेष हैश अर्थ विज्ञान चाहते हैं, और __cmp__या __eq__आदेश में एक कुंजी के रूप में अपनी कक्षा में प्रयोग करने योग्य बनाने के लिए। समान की तुलना करने वाली वस्तुओं का समान हैश मान होना आवश्यक है।

अजगर __hash__एक पूर्णांक लौटने की उम्मीद करता है, लौटने Banana()की सिफारिश नहीं की जाती है :)

उपयोगकर्ता परिभाषित कक्षाओं में __hash__डिफ़ॉल्ट रूप से कॉल होता है id(self), जैसा आपने नोट किया है।

प्रलेखन से कुछ अतिरिक्त सुझाव दिए गए हैं ।

ऐसी कक्षाएं जो __hash__() किसी मूल वर्ग से एक विधि प्राप्त करती हैं, लेकिन __cmp__()या का अर्थ बदल देती हैं, __eq__() जैसे कि लौटाया गया हैश मान अब उपयुक्त नहीं है (जैसे कि डिफ़ॉल्ट पहचान आधारित समानता के बजाय समानता के मूल्य-आधारित अवधारणा पर स्विच करके) स्पष्ट रूप से स्वयं को चिह्नित कर सकते हैं __hash__ = None कक्षा की परिभाषा में सेट करके अप्राप्य होना । ऐसा करने का अर्थ है कि न केवल कक्षा के उदाहरण एक उपयुक्त टाइप-इयरर को बढ़ाते हैं, जब कोई प्रोग्राम उनके हैश मान को पुनः प्राप्त करने का प्रयास करता है, बल्कि चेक करते समय उन्हें सही ढंग से अस्वाभाविक रूप से पहचाना भी जाएगा isinstance(obj, collections.Hashable) (वर्गों के विपरीत जो __hash__()स्पष्ट रूप से उठाने के लिए अपने स्वयं को परिभाषित करते हैं )।


2
अकेले हैश पर्याप्त नहीं है, इसके अतिरिक्त आपको या तो ओवरराइड करने की आवश्यकता है __eq__या __cmp__
ओबेन सोन

@ ओबेन सोन: __cmp__आपको पायथन द्वारा दिया जाता है यदि यह एक उपयोगकर्ता परिभाषित वर्ग है, लेकिन आप शायद नए शब्दार्थों के लिए समायोजित करने के लिए उन्हें ओवरराइड करना चाहते हैं।
स्केरमेडल

1
@Skurmedel: हां, लेकिन यद्यपि आप उन उपयोगकर्ता वर्गों पर कॉल cmpऔर उपयोग =कर सकते हैं जो इन विधियों को ओवरराइड नहीं करते हैं, उनमें से एक को प्रश्नकर्ता की आवश्यकता को पूरा करने के लिए लागू किया जाना चाहिए कि समान नाम और स्थान के साथ उदाहरणों में समान शब्दकोश कुंजी है।
ओबेन सोनें
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.