पायथन: डिफॉल्ट का डिफाल्ट?


323

defaultdict(defaultdict(int))निम्नलिखित कोड काम करने के लिए क्या कोई तरीका है ?

for x in stuff:
    d[x.a][x.b] += x.c_int

dx.aऔर x.bतत्वों के आधार पर तदर्थ का निर्माण किया जाना चाहिए ।

मैं प्रयोग कर सकता हूँ:

for x in stuff:
    d[x.a,x.b] += x.c_int

लेकिन तब मैं उपयोग नहीं कर पाऊंगा:

d.keys()
d[x.a].keys()

6
समान प्रश्न देखें पायथन में नेस्टेड शब्दकोशों को लागू करने का सबसे अच्छा तरीका क्या है? ऑटिविविफिकेशन पर विकिपीडिया के लेख में संभवतः कुछ उपयोगी जानकारी भी है ।
मार्टिउ

जवाबों:


571

हां मुझे यह पंसद है:

defaultdict(lambda: defaultdict(int))

जब आप मौजूद कुंजी को एक्सेस करने का प्रयास करते हैं, तो defaultdict(इस मामले में lambda: defaultdict(int)) का तर्क कहा जाएगा। इसका वापसी मूल्य इस कुंजी के नए मूल्य के रूप में सेट किया जाएगा, जिसका अर्थ है कि हमारे मामले में d[Key_doesnt_exist]इच्छा का मूल्य होगा defaultdict(int)

यदि आप इस अंतिम डिफॉल्टडिस्क से एक कुंजी को एक्सेस करने का प्रयास करते हैं, तो d[Key_doesnt_exist][Key_doesnt_exist]यह 0 पर वापस आ जाएगा, जो कि अंतिम डिफॉल्टडिक्ट के तर्क का रिटर्न वैल्यू है int()


7
यह बहुत अच्छा काम करता है! क्या आप इस वाक्यविन्यास के पीछे तर्कसंगत व्याख्या कर सकते हैं?
जोनाथन

37
@ जोनाथन: हाँ, निश्चित रूप से, defaultdict(इस मामले में lambda : defaultdict(int)) का तर्क उस समय कहा जाएगा जब आप उस कुंजी को एक्सेस करने का प्रयास करेंगे जो मौजूद नहीं है और उसका रिटर्न मान इस कुंजी के नए मूल्य के रूप में सेट किया जाएगा, जिसका अर्थ है हमारे मामले का मूल्य d[Key_dont_exist]हो जाएगा defaultdict(int), और यदि आप यह पिछले defaultdict से एक चाबी का उपयोग करने की कोशिश यानी d[Key_dont_exist][Key_dont_exist]यह 0 जो पिछले के तर्क के रिटर्न मान है वापस आ जाएगी defaultdictयानी int(), आशा है यह मदद मिली।
मौद

25
तर्क defaultdictएक समारोह होना चाहिए। defaultdict(int)एक शब्दकोश है, जबकि lambda: defaultdict(int)फ़ंक्शन है जो एक शब्दकोश देता है।
has2k1

27
@ has2k1 यह गलत है। डिफॉल्ट का तर्क कॉल करने योग्य होना चाहिए। एक लंबोदर एक कॉल करने योग्य है।
नील्स बॉम

2
@RickyLevi, अगर आप चाहते हैं कि काम करने से आप बस इतना कह defaultdict(lambda: defaultdict(lambda: defaultdict(int)))
सकें

51

डिफ़ॉल्ट डिफेंडर का पैरामीटर फ़ंक्शन है जिसे नए तत्वों के निर्माण के लिए बुलाया जाएगा। तो चलो एक मेमने का उपयोग करें!

>>> from collections import defaultdict
>>> d = defaultdict(lambda : defaultdict(int))
>>> print d[0]
defaultdict(<type 'int'>, {})
>>> print d[0]["x"]
0

पायथन 2.7 के बाद से, ए काउंटर का उपयोग करके भी बेहतर समाधान है :

>>> from collections import Counter
>>> c = Counter()
>>> c["goodbye"]+=1
>>> c["and thank you"]=42
>>> c["for the fish"]-=5
>>> c
Counter({'and thank you': 42, 'goodbye': 1, 'for the fish': -5})

कुछ बोनस सुविधाएँ

>>> c.most_common()[:2]
[('and thank you', 42), ('goodbye', 1)]

अधिक जानकारी के लिए PyMOTW - संग्रह - कंटेनर डेटा प्रकार और पायथन प्रलेखन - संग्रह देखें


5
बस यहां सर्कल को पूरा करने के लिए, आप विशेष रूप से समस्या को मूल रूप से हल करने के d = defaultdict(lambda : Counter())बजाय उपयोग करना चाहेंगे d = defaultdict(lambda : defaultdict(int))
गम

3
@ आप d = defaultdict(Counter())इस मामले में केवल एक लंबोदर की आवश्यकता का उपयोग नहीं कर सकते
देब

3
@ आपके पास एक छोटी सी त्रुटि है- आंतरिक कोष्ठकों को हटा दें ताकि आप एक Counterवस्तु के बजाय एक कॉल करने योग्य पास करें । वह है:d = defaultdict(Counter)
डिलन डेविस

29

मुझे यह उपयोग करने के लिए थोड़ा और सुरुचिपूर्ण लगता है partial:

import functools
dd_int = functools.partial(defaultdict, int)
defaultdict(dd_int)

बेशक, यह एक मेमने के समान है।


1
आंशिक यहाँ लंबोदा से भी बेहतर है क्योंकि इसे पुनरावर्ती रूप से लागू किया जा सकता है :) जेनेरिक नेस्टेड डिफॉल्ट फ़ैक्ट विधि के लिए मेरा उत्तर नीचे देखें।
कैम्पि

@Campi आपको पुनरावर्ती अनुप्रयोगों के लिए आंशिक रूप से ज़रूरत नहीं है, AFAICT
Clément

10

संदर्भ के लिए, जेनेरिक नेस्टेड defaultdictफैक्ट्री विधि को लागू करना संभव है :

from collections import defaultdict
from functools import partial
from itertools import repeat


def nested_defaultdict(default_factory, depth=1):
    result = partial(defaultdict, default_factory)
    for _ in repeat(None, depth - 1):
        result = partial(defaultdict, result)
    return result()

गहराई परिभाषित प्रकार default_factoryका उपयोग करने से पहले नेस्टेड शब्दकोश की संख्या को परिभाषित करती है। उदाहरण के लिए:

my_dict = nested_defaultdict(list, 3)
my_dict['a']['b']['c'].append('e')

क्या आप एक उपयोग उदाहरण दे सकते हैं? जिस तरह से मैं यह उम्मीद करने के लिए काम नहीं कर रहा। ndd = nested_defaultdict(dict) .... ndd['a']['b']['c']['d'] = 'e'थ्रोKeyError: 'b'
डेविड मार्क्स

हे डेविड, आपको अपने डिक्शनरी की गहराई को अपने उदाहरण 3 में परिभाषित करने की आवश्यकता है (जैसा कि आपने डिफॉल्ट_फैक्टिंग को डिक्शनरी के रूप में परिभाषित किया है। nested_defaultdict (तानाशाह, 3) आपके लिए काम करेगा।
Campi

यह सुपर उपयोगी था, धन्यवाद! एक बात जिस पर मैंने गौर किया है कि यह एक default_dict बनाता है depth=0, जिसे कॉलिंग के समय गहराई अज्ञात होने पर हमेशा वांछित नहीं किया जा सकता है। if not depth: return default_factory()फ़ंक्शन के शीर्ष पर, एक पंक्ति जोड़कर आसानी से ठीक किया जा सकता है, हालांकि संभवतः अधिक सुरुचिपूर्ण समाधान है।
ब्रेंडन

9

पिछले उत्तरों ने संबोधित किया है कि दो-स्तरीय या एन-स्तर कैसे बनाएं defaultdict। कुछ मामलों में आप एक अनंत चाहते हैं:

def ddict():
    return defaultdict(ddict)

उपयोग:

>>> d = ddict()
>>> d[1]['a'][True] = 0.5
>>> d[1]['b'] = 3
>>> import pprint; pprint.pprint(d)
defaultdict(<function ddict at 0x7fcac68bf048>,
            {1: defaultdict(<function ddict at 0x7fcac68bf048>,
                            {'a': defaultdict(<function ddict at 0x7fcac68bf048>,
                                              {True: 0.5}),
                             'b': 3})})

1
मुझे यह पसंद है। यह शैतानी सरल है, लेकिन अविश्वसनीय रूप से उपयोगी है। धन्यवाद!
रोज़स्टेक्स

6

दूसरों ने आपके प्रश्न का सही उत्तर दिया है कि निम्नलिखित कार्य कैसे करें:

for x in stuff:
    d[x.a][x.b] += x.c_int

कुंजी के लिए एक विकल्प का उपयोग करना होगा:

d = defaultdict(int)
for x in stuff:
    d[x.a,x.b] += x.c_int
    # ^^^^^^^ tuple key

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


4
इस समाधान का अर्थ है कि यह सभी d [xa] को प्राप्त करने के लिए सरल नहीं है, क्योंकि आपको यह देखने के लिए कि क्या यह tuple के पहले तत्व के रूप में xa है, हर कुंजी को आत्मनिरीक्षण करने की आवश्यकता है।
मैथ्यू Schinckel

5
यदि आप 3 स्तरों को नेस्टिंग करना चाहते हैं, तो बस इसे 3 स्तरों के रूप में परिभाषित करें: d = defaultdict (lambda: defaultdict (lambda: defaultdict (int)))
मैथ्यू Schinckel
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.