मैं अजगर में एक प्रमुख कुंजी के रूप में एक सूची का उपयोग क्यों नहीं कर सकता हूं?


101

मैं इस बारे में थोड़ा उलझन में हूं कि एक अजगर के लिए एक कुंजी के रूप में क्या इस्तेमाल किया जा सकता है / नहीं।

dicked = {}
dicked[None] = 'foo'     # None ok
dicked[(1,3)] = 'baz'    # tuple ok
import sys
dicked[sys] = 'bar'      # wow, even a module is ok !
dicked[(1,[3])] = 'qux'  # oops, not allowed

तो एक टपल एक अपरिवर्तनीय प्रकार है, लेकिन अगर मैं इसके अंदर एक सूची छिपाता हूं, तो यह एक कुंजी नहीं हो सकती है .. क्या मैं आसानी से एक मॉड्यूल के अंदर सूची को छिपा नहीं सकता?

मुझे कुछ अस्पष्ट विचार आया था कि कुंजी को "हैशेबल" होना है लेकिन मैं तकनीकी विवरण के बारे में अपनी अज्ञानता को स्वीकार करने जा रहा हूं; मुझे नहीं पता कि यहां वास्तव में क्या हो रहा है। यदि आप सूचियों को कुंजी के रूप में उपयोग करने की कोशिश करते हैं, तो क्या होगा, जैसे कि, उनकी मेमोरी लोकेशन?


1
यहाँ एक अच्छी चर्चा है: stackoverflow.com/questions/2671211/…
Hernan

49
आपके चर नाम से एक चकली मिली।
टाइप करें

जवाबों:


34

पायथन विकी में इस विषय पर एक अच्छा लेख है: लिस्ट्स कैन बी बी डिक्शनरी कीज़ । जैसा कि वहां बताया गया है:

यदि आप सूचियों को कुंजी के रूप में उपयोग करने की कोशिश करते हैं, तो क्या होगा, जैसे कि, उनकी मेमोरी लोकेशन?

यह वास्तव में किसी भी आवश्यकताओं को तोड़ने के बिना किया जा सकता है, लेकिन यह अप्रत्याशित व्यवहार की ओर जाता है। सूचियों को आम तौर पर माना जाता है जैसे कि उनका मूल्य उनकी सामग्री के मूल्यों से लिया गया था, उदाहरण के लिए जब जाँच (में) समानता। बहुत से - समझदारी से - अपेक्षा करें कि आप किसी भी सूची [1, 2]का उपयोग उसी कुंजी को प्राप्त करने के लिए कर सकते हैं , जहाँ आपको लगभग उसी सूची ऑब्जेक्ट को रखना होगा। लेकिन जैसे ही कुंजी के रूप में उपयोग की गई सूची को संशोधित किया जाता है, और पहचान द्वारा देखने के लिए वैल्यू ब्रेक द्वारा देखने के लिए आपको लगभग उसी सूची को बनाए रखने की आवश्यकता होती है - जिसके लिए किसी अन्य सामान्य सूची संचालन की आवश्यकता नहीं है (कम से कम कोई भी मैं सोच भी नहीं सकता )।

अन्य वस्तुएं जैसे कि मॉड्यूल और objectवैसे भी उनकी वस्तु पहचान से बहुत बड़ा सौदा करते हैं (जब पिछली बार आपके पास दो अलग-अलग मॉड्यूल ऑब्जेक्ट थे sys?), और उनकी तुलना वैसे भी की जाती है। इसलिए, यह कम आश्चर्य की बात है - या यहां तक ​​कि उम्मीद की जाती है - कि वे, जब तानाशाही कुंजी के रूप में उपयोग किया जाता है, तो उस मामले में भी पहचान से तुलना करें।


31

मैं अजगर में एक प्रमुख कुंजी के रूप में एक सूची का उपयोग क्यों नहीं कर सकता हूं?

>>> d = {repr([1,2,3]): 'value'}
{'[1, 2, 3]': 'value'}

(जो कोई इस सवाल पर ठोकर खाता है, उसके चारों ओर एक रास्ता खोज रहा है)

जैसा कि यहां दूसरों द्वारा समझाया गया है, वास्तव में आप नहीं कर सकते। यदि आप वास्तव में अपनी सूची का उपयोग करना चाहते हैं तो आप इसके स्ट्रिंग प्रतिनिधित्व का उपयोग कर सकते हैं।


5
क्षमा करें, मैं वास्तव में आपकी बात नहीं देखता। यह कुंजी के रूप में स्ट्रिंग शाब्दिक का उपयोग करने के लिए अलग नहीं है।
विम

11
सच; मैंने अभी तक बहुत सारे उत्तर देखे हैं कि वास्तव में आप यह समझाते हैं कि आप सूचियों का उपयोग 'महत्वपूर्ण होना चाहिए' के ​​संदर्भ में क्यों नहीं कर सकते हैं, जो इतना सच है, कि मैं इसके चारों ओर एक रास्ता सुझाना चाहता था, बस किसी मामले में (नया) इसकी तलाश में होगा ...
रेमी

5
सिर्फ सूची को टूप्ले में क्यों न बदलें? इसे स्ट्रिंग में क्यों बदलें? यदि आप एक टपल का उपयोग करते हैं, तो यह उन कक्षाओं के साथ सही ढंग से काम करेगा जिनके पास एक कस्टम तुलना पद्धति है __eq__। लेकिन अगर आप उन्हें स्ट्रिंग में परिवर्तित करते हैं, तो सब कुछ इसकी स्ट्रिंग प्रतिनिधित्व द्वारा तुलना की जाती है।
अरन-फे

अच्छी बात @ अरन-फे। बस यह सुनिश्चित कर लें कि टपल में कोई भी तत्व अपने आप ही धुलाई योग्य है। जैसे टुपल ([[1,2], [2,3]]) एक कुंजी के रूप में काम नहीं करेगा क्योंकि टपल के तत्व अभी भी सूची में हैं।
रेमी

17

बस पाया गया कि आप सूची को तुच्छ में बदल सकते हैं, फिर इसे कुंजी के रूप में उपयोग करें।

d = {tuple([1,2,3]): 'value'}

15

मुद्दा यह है कि ट्यूपल्स अपरिवर्तनीय हैं, और सूचियां नहीं हैं। निम्नलिखित को धयान मे रखते हुए

d = {}
li = [1,2,3]
d[li] = 5
li.append(4)

क्या d[li]लौटना चाहिए ? क्या यह वही सूची है? कैसे के बारे में d[[1,2,3]]? इसके समान मूल्य हैं, लेकिन एक अलग सूची है?

अंत में, कोई संतोषजनक जवाब नहीं है। उदाहरण के लिए, यदि एकमात्र कुंजी जो काम करती है वह मूल कुंजी है, तो यदि आपके पास उस कुंजी का कोई संदर्भ नहीं है, तो आप फिर से मूल्य तक नहीं पहुंच सकते हैं। प्रत्येक अन्य अनुमत कुंजी के साथ, आप मूल के संदर्भ के बिना एक कुंजी का निर्माण कर सकते हैं।

यदि मेरे सुझाव दोनों काम करते हैं, तो आपके पास बहुत अलग चाबियाँ हैं जो एक ही मूल्य लौटाती हैं, जो थोड़ा आश्चर्य से अधिक है। यदि केवल मूल सामग्री ही काम करती है, तो आपकी कुंजी जल्दी खराब हो जाएगी, क्योंकि सूचियों को संशोधित किया जाता है।


हां, यह एक ही सूची है, इसलिए मैं d[li]5 बने रहने की उम्मीद d[[1,2,3]]करूंगा। कुंजी के रूप में एक अलग सूची ऑब्जेक्ट को संदर्भित करेगा, इसलिए यह एक KeyEror होगा। मुझे वास्तव में अभी तक कोई समस्या नहीं दिखाई दे रही है .. सिवाय इसके कि एक कुंजी को इकट्ठा किया गया कचरा इकट्ठा करने से कुछ प्रमुख मूल्य अप्राप्य हो सकते हैं। लेकिन यह एक व्यावहारिक समस्या एक तार्किक समस्या नहीं है ..
विम

@wim: d[list(li)]KeyError होना समस्या का हिस्सा है। लगभग हर दूसरे उपयोग के मामले में , liसमान सामग्री वाली एक नई सूची से अप्रभेद्य होगा। यह काम करता है, लेकिन यह कई लोगों के लिए काउंटर-सहज है। इसके अलावा, जब पिछली बार आपको वास्तव में तानाशाही कुंजी के रूप में एक सूची का उपयोग करना था? केवल एक ही उपयोग का मामला जिसकी मैं कल्पना कर सकता हूं, जब आप किसी भी तरह से पहचान के आधार पर सब कुछ कर रहे हैं, और उस मामले में आपको बस उस पर भरोसा करने __hash__और __eq__पहचान आधारित होने के बजाय ऐसा करना चाहिए ।

@delnan क्या समस्या सिर्फ इतनी है कि ऐसी जटिलताओं के कारण यह बहुत उपयोगी नहीं होगा? या क्या कोई कारण है कि यह वास्तव में एक तानाशाही को तोड़ सकता है?
विम

1
@wim: बाद वाला। जैसा कि मेरे जवाब में कहा गया है, यह वास्तव में तानाशाही चाबियों पर आवश्यकताओं को नहीं तोड़ता है, लेकिन यह हल करने की तुलना में अधिक समस्याओं को पेश करने की संभावना है।

1
@ डायलेन - आपको 'पूर्व' कहने का मतलब था
जेसन

9

यहाँ एक उत्तर दिया गया है http://wiki.python.org/moin/DictionaryKeys

यदि आप सूचियों को कुंजी के रूप में उपयोग करने की कोशिश करते हैं, तो क्या होगा, जैसे कि, उनकी मेमोरी लोकेशन?

समान सामग्रियों के साथ अलग-अलग सूचियों को देखने से अलग-अलग परिणाम प्राप्त होंगे, भले ही समान सामग्रियों के साथ सूचियों की तुलना करना उन्हें समतुल्य इंगित करेगा।

एक शब्दकोश देखने में एक सूची शाब्दिक का उपयोग करने के बारे में क्या?


3

आपका अवेयरनैस यहां पाया जा सकता है:

क्यों सूचियाँ शब्दकोश कुंजी नहीं हो सकती

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

स्रोत और अधिक जानकारी: http://wiki.python.org/moin/DictionaryKeys


3

चूँकि सूचियाँ उत्परिवर्तनीय हैं, dictकुंजी (और setसदस्यों) को धोने योग्य होने की आवश्यकता है, और हैशिंग उत्परिवर्तित वस्तुएँ एक बुरा विचार है क्योंकि हैश मान होना चाहिए उदाहरण विशेषताओं के आधार पर गणना की चाहिए।

इस जवाब में, मैं कुछ ठोस उदाहरण दूंगा, उम्मीद है कि मौजूदा उत्तरों के शीर्ष पर मूल्य को जोड़ना होगा। प्रत्येक जानकारी setडेटास्ट्रक्चर के तत्वों पर भी लागू होती है ।

उदाहरण 1 : हैशबल एक उत्परिवर्तित वस्तु है, जहां हैश का मूल्य वस्तु की एक परस्पर विशेषता पर आधारित है।

>>> class stupidlist(list):
...     def __hash__(self):
...         return len(self)
... 
>>> stupid = stupidlist([1, 2, 3])
>>> d = {stupid: 0}
>>> stupid.append(4)
>>> stupid
[1, 2, 3, 4]
>>> d
{[1, 2, 3, 4]: 0}
>>> stupid in d
False
>>> stupid in d.keys()
False
>>> stupid in list(d.keys())
True

उत्परिवर्तन के बाद stupid, इसे किसी भी लंबे समय तक हुक में नहीं पाया जा सकता है क्योंकि हैश बदल गया है। तानाशाह की चाबी की सूची पर केवल एक रेखीय स्कैन पाता है stupid

उदाहरण 2 : ... लेकिन सिर्फ एक स्थिर हैश मान क्यों नहीं?

>>> class stupidlist2(list):
...     def __hash__(self):
...         return id(self)
... 
>>> stupidA = stupidlist2([1, 2, 3])
>>> stupidB = stupidlist2([1, 2, 3])
>>> 
>>> stupidA == stupidB
True
>>> stupidA in {stupidB: 0}
False

यह एक अच्छा विचार के रूप में अच्छी तरह से क्योंकि बराबर वस्तुओं हूबहू इस तरह है कि आप उन्हें में एक पा सकते हैं हैश चाहिए नहीं है dictया set

उदाहरण 3 : ... ठीक है, सभी उदाहरणों में निरंतर हैश के बारे में क्या है ?!

>>> class stupidlist3(list):
...     def __hash__(self):
...         return 1
... 
>>> stupidC = stupidlist3([1, 2, 3])
>>> stupidD = stupidlist3([1, 2, 3])
>>> stupidE = stupidlist3([1, 2, 3, 4])
>>> 
>>> stupidC in {stupidD: 0}
True
>>> stupidC in {stupidE: 0}
False
>>> d = {stupidC: 0}
>>> stupidC.append(5)
>>> stupidC in d
True

चीजें अपेक्षा के अनुरूप काम करने लगती हैं, लेकिन सोचें कि क्या हो रहा है: जब आपकी कक्षा के सभी उदाहरण समान हैश मान का उत्पादन करते हैं, तो आपके पास हैश की टक्कर तब होगी जब दो से अधिक उदाहरण होंगे जैसे कि dictया एक में मौजूद कुंजीset

my_dict[key]या key in my_dict(या item in my_set) के साथ सही उदाहरण खोजने के लिए कई समानता की जाँच करने की आवश्यकता है क्योंकि वहाँ के उदाहरण हैंstupidlist3 कि तानाशाही कीज़ (सबसे खराब स्थिति में) के उदाहरण हैं। इस बिंदु पर, शब्दकोश का उद्देश्य - ओ (1) देखने - पूरी तरह से हार गया है। यह निम्नलिखित समय (IPython के साथ किया गया) में प्रदर्शित होता है।

उदाहरण 3 के लिए कुछ समय

>>> lists_list = [[i]  for i in range(1000)]
>>> stupidlists_set = {stupidlist3([i]) for i in range(1000)}
>>> tuples_set = {(i,) for i in range(1000)}
>>> l = [999]
>>> s = stupidlist3([999])
>>> t = (999,)
>>> 
>>> %timeit l in lists_list
25.5 µs ± 442 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> %timeit s in stupidlists_set
38.5 µs ± 61.2 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> %timeit t in tuples_set
77.6 ns ± 1.5 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

जैसा कि आप देख सकते हैं, हमारे में सदस्यता परीक्षण stupidlists_setपूरी तरह से एक रैखिक स्कैन की तुलना में भी धीमा है lists_list, जबकि आपके पास हैश टकराव के भार के बिना एक सेट में अपेक्षित सुपर फास्ट लुकअप समय (कारक 500) है।


टी एल; DR: आप चाबियों के tuple(yourlist)रूप में उपयोग कर सकते हैं dict, क्योंकि ट्यूप्स अपरिवर्तनीय और धोने योग्य हैं।


>>> x = (1,2,3321321321321,) >>> आईडी (x) 139936535758888 >>> z = (1,2,3321321321321,) >>> आईडी (z) 139936535760544 >>> आईडी (1) 2,3321321321321,)) 139936535810768 इन 3 में समान टपल मूल्य हैं, लेकिन अलग-अलग आईडी हैं। तो कुंजी x के साथ एक शब्दकोष में कुंजी z के लिए कोई मूल्य नहीं होगा?
अश्विनी

@ अश्वनी क्या आपने इसे आज़माया?
टाइमजैब

हां, यह अपेक्षा के अनुरूप काम कर रहा है, मेरा संदेह सभी ट्यूपल्स के समान मूल्यों के साथ अलग-अलग आईडी हैं। तो इस हैश की गणना किस आधार पर की जाती है?
अश्विनी

@ अश्वनी का हैश xऔर zएक ही है। यदि इस बारे में कुछ स्पष्ट नहीं है, तो कृपया एक नया प्रश्न खोलें।
टाइमजैब

1
@Ashwani hash(x)और hash(z)
टाइमजैब

1

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

एक साइड नोट के रूप में, डिक्टोबोबेक्ट्स लुकअप का वास्तविक कार्यान्वयन नुटहॉल वॉल्यूम से एल्गोरिथ्म डी पर आधारित है। 3, सेक। 6.4। यदि आपके पास वह पुस्तक उपलब्ध है, तो यह एक सार्थक पाठ हो सकता है, इसके अलावा यदि आप वास्तव में हैं, तो वास्तव में आपकी रुचि है कि आप यहां डिक्टोबोबजेक्ट के वास्तविक कार्यान्वयन पर डेवलपर टिप्पणियों पर एक नज़र डाल सकते हैं। यह महान विस्तार में जाता है कि यह कैसे काम करता है। शब्दकोशों के कार्यान्वयन पर एक अजगर व्याख्यान भी है, जिसमें आपकी रुचि हो सकती है। वे एक कुंजी की परिभाषा से गुजरते हैं और पहले कुछ मिनटों में एक हैश क्या होता है।


-1

अजगर 2.7.2 प्रलेखन के अनुसार:

यदि कोई हैश वैल्यू है तो एक ऑब्जेक्ट हैशेबल है, जो उसके जीवनकाल के दौरान कभी नहीं बदलता है (इसे हैश () विधि की आवश्यकता होती है), और इसकी तुलना अन्य वस्तुओं से की जा सकती है (इसे eq () या cmp () विधि) की आवश्यकता होती है। हेशिबल ऑब्जेक्ट जो समान की तुलना करते हैं उनके पास समान हैश मान होना चाहिए।

Hashability किसी वस्तु को शब्दकोश कुंजी और सेट सदस्य के रूप में प्रयोग करने योग्य बनाता है, क्योंकि ये डेटा संरचनाएँ आंतरिक रूप से हैश मान का उपयोग करती हैं।

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

एक ट्यूपल इस अर्थ में अपरिवर्तनीय है कि आप इसके तत्वों को जोड़, हटा या प्रतिस्थापित नहीं कर सकते हैं, लेकिन तत्व स्वयं परस्पर हो सकते हैं। सूची का हैश मान उसके तत्वों के हैश मूल्यों पर निर्भर करता है, और इसलिए जब आप तत्वों को बदलते हैं तो यह बदल जाता है।

आईडी के लिए हैश का उपयोग करने का अर्थ यह होगा कि सभी सूचियाँ अलग-अलग तुलना करती हैं, जो आश्चर्यजनक और असुविधाजनक होगी।


1
यह सवाल का जवाब नहीं है, यह करता है? hash = idपहले पैराग्राफ के अंत में अपरिवर्तनीय को नहीं तोड़ता है, सवाल यह है कि ऐसा क्यों नहीं किया गया है।

@ डायलेन: मैंने स्पष्ट करने के लिए अंतिम पैराग्राफ जोड़ा।
निकोला मुसत्ती

-1

एक शब्दकोश एक हैशपॉप है जो आपकी कुंजी के नक्शे को संग्रहीत करता है, मूल्य हैशेड नई कुंजी और मूल्य मैपिंग में परिवर्तित होता है।

कुछ ऐसा है (पीडो कोड):

{key : val}  
hash(key) = val

यदि आप सोच रहे हैं कि कौन से विकल्प उपलब्ध हैं जो आपके शब्दकोश की कुंजी के रूप में उपयोग किए जा सकते हैं। फिर

ऐसी कोई भी चीज़ जो हैशेबल हो (हैश में परिवर्तित की जा सकती है, और स्टैटिक वैल्यू यानी अपरिवर्तनीय है, ताकि ऊपर बताई गई हैश की कुंजी बनायी जा सके) पात्र है, लेकिन सूची या सेट की गई वस्तुओं के चलते समय अलग-अलग हो सकता है, इसलिए हैश (कुंजी) की भी आवश्यकता होनी चाहिए बस अपनी सूची या सेट के साथ सिंक में भिन्न होने के लिए।

तुम कोशिश कर सकते हो :

hash(<your key here>)

यदि यह ठीक काम करता है तो इसे आपके शब्दकोश की कुंजी के रूप में इस्तेमाल किया जा सकता है या फिर इसे किसी चीज़ से धो सकते हैं।


संक्षेप में :

  1. उस सूची में कनवर्ट करें tuple(<your list>)
  2. उस सूची में कनवर्ट करें str(<your list>)

-1

dictकुंजी को धोने योग्य होना चाहिए। सूचियाँ म्यूटेबल हैं और वे एक वैध हैश विधि प्रदान नहीं करते हैं ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.