पायथन में 'collection.defaultdict ’के कई स्तर


176

एसओ पर कुछ महान लोगों के लिए धन्यवाद, मैंने संभावनाओं की खोज की collections.defaultdict , विशेष रूप से पठनीयता और गति में। मैंने उन्हें सफलता के साथ उपयोग करने के लिए रखा है।

अब मैं शब्दकोशों के तीन स्तरों को लागू करना चाहूंगा, दो शीर्ष व्यक्ति defaultdictऔर सबसे कम एक को int। मुझे ऐसा करने का उपयुक्त तरीका नहीं मिला। यहाँ मेरा प्रयास है:

from collections import defaultdict
d = defaultdict(defaultdict)
a = [("key1", {"a1":22, "a2":33}),
     ("key2", {"a1":32, "a2":55}),
     ("key3", {"a1":43, "a2":44})]
for i in a:
    d[i[0]] = i[1]

अब यह काम करता है, लेकिन निम्नलिखित, जो वांछित व्यवहार है:

d["key4"]["a1"] + 1

मुझे संदेह है कि मुझे कहीं घोषित करना चाहिए था कि दूसरा स्तर defaultdictप्रकार का है int, लेकिन मुझे यह नहीं पता था कि ऐसा कहां या कैसे करना है।

defaultdictपहली जगह का उपयोग करने का कारण यह है कि प्रत्येक नई कुंजी के लिए शब्दकोश को आरंभ करने से बचने के लिए।

कोई और सुरुचिपूर्ण सुझाव?

धन्यवाद पायथनर्स!

जवाबों:


341

उपयोग:

from collections import defaultdict
d = defaultdict(lambda: defaultdict(int))

defaultdict(int)जब भी कोई नई कुंजी एक्सेस की जाती है तो यह एक नया निर्माण करेगा d


2
केवल समस्या यह है कि यह अचार नहीं होगा, इसका अर्थ multiprocessingयह है कि ये आगे और पीछे भेजने से नाखुश हैं।
नूह

19
@ नोहा: अगर आप लैंबडा के बजाय एक नामित मॉड्यूल-स्तर फ़ंक्शन का उपयोग करते हैं, तो यह अचार करेगा।
इंटरजाइ

4
@ScienceFriction कुछ भी विशिष्ट है जिसकी आपको सहायता चाहिए? जब d[new_key]एक्सेस किया जाता है, तो यह लैम्बडा को कॉल करेगा जो एक नया निर्माण करेगा defaultdict(int)। और जब d[existing_key][new_key2]एक्सेस किया जाता है, तो एक नया intबनाया जाएगा।
इंटरजेट

11
यह कमाल का है। ऐसा लगता है कि मैं रोजाना अपने वैवाहिक जीवन को नवीनीकृत करता हूं।
एमवीसीएचआर

3
इस पद्धति का उपयोग करने के बारे में अधिक जानकारी की तलाश में multiprocessingऔर एक नामित मॉड्यूल-स्तर फ़ंक्शन क्या है? यह प्रश्न इस प्रकार है।
सेसिलिया

32

एक नमकीन बनाने के लिए एक और तरीका, नेस्टेड डिफाल्डिक्ट एक लंबो के बजाय एक आंशिक वस्तु का उपयोग करना है:

from functools import partial
...
d = defaultdict(partial(defaultdict, int))

यह काम करेगा क्योंकि डिफ़ॉल्ट स्तर पर मॉड्यूल स्तर पर डिफ़ॉल्ट रूप से सुलभ है:

"आप एक आंशिक वस्तु को तब तक अचार नहीं कर सकते हैं जब तक कि फ़ंक्शन [या इस मामले में, वर्ग] यह विश्व स्तर पर सुलभ न हो ... इसके __name__ (इसके __module__ के भीतर) के तहत" - अचार लपेटा गया आंशिक कार्य


12

अधिक सामान्य समाधान के लिए यहाँ nosklo के उत्तर को देखें ।

class AutoVivification(dict):
    """Implementation of perl's autovivification feature."""
    def __getitem__(self, item):
        try:
            return dict.__getitem__(self, item)
        except KeyError:
            value = self[item] = type(self)()
            return value

परिक्षण:

a = AutoVivification()

a[1][2][3] = 4
a[1][3][3] = 5
a[1][2]['test'] = 6

print a

आउटपुट:

{1: {2: {'test': 6, 3: 4}, 3: {3: 5}}}

लिंक के लिए धन्यवाद @ mi82 (और संपादित करें, @voyager)। यह दृष्टिकोण कितना आकर्षक और सुरक्षित है?
मॉरलॉक

2
दुर्भाग्य से यह समाधान डिफॉल्ट के सबसे अच्छे हिस्से को संरक्षित नहीं करता है, जो कि कुंजी के अस्तित्व की चिंता किए बिना डी ['कुंजी'] + = 1 जैसी कुछ लिखने की शक्ति है। यह मुख्य विशेषता है जिसके लिए मैं डिफ़ॉल्ट का उपयोग करता हूं ... लेकिन मैं कल्पना कर सकता हूं कि गतिशील रूप से गहन शब्दकोश बहुत आसान हैं।
rschwieb

2
@rschwieb आप जोड़कर लिखने + = 1 के लिए बिजली जोड़ सकते हैं जोड़ने विधि।
स्पजम

5

के लिए @ rschwieb के अनुरोध के अनुसार D['key'] += 1, हम परिभाषित करके अतिरिक्त जोड़कर पिछले पर विस्तार कर सकते हैं__add__ विधि , इस तरह के व्यवहार को और अधिक बनाने के लिएcollections.Counter()

पहले __missing__एक नया खाली मान बनाने के लिए कहा जाएगा, जिसे पारित किया जाएगा __add__। हम मान का परीक्षण करते हैं, होने के लिए खाली मानों की गिनती करते हैंFalse

ओवरराइडिंग के बारे में अधिक जानकारी के लिए संख्यात्मक प्रकारों का अनुकरण करना देखें ।

from numbers import Number


class autovivify(dict):
    def __missing__(self, key):
        value = self[key] = type(self)()
        return value

    def __add__(self, x):
        """ override addition for numeric types when self is empty """
        if not self and isinstance(x, Number):
            return x
        raise ValueError

    def __sub__(self, x):
        if not self and isinstance(x, Number):
            return -1 * x
        raise ValueError

उदाहरण:

>>> import autovivify
>>> a = autovivify.autovivify()
>>> a
{}
>>> a[2]
{}
>>> a
{2: {}}
>>> a[4] += 1
>>> a[5][3][2] -= 1
>>> a
{2: {}, 4: 1, 5: {3: {2: -1}}}

तर्क की जाँच करने के बजाय एक संख्या है (बहुत ही गैर-अजगर, अमीर!) हम बस एक डिफ़ॉल्ट 0 मान प्रदान कर सकते हैं और फिर ऑपरेशन का प्रयास कर सकते हैं:

class av2(dict):
    def __missing__(self, key):
        value = self[key] = type(self)()
        return value

    def __add__(self, x):
        """ override addition when self is empty """
        if not self:
            return 0 + x
        raise ValueError

    def __sub__(self, x):
        """ override subtraction when self is empty """
        if not self:
            return 0 - x
        raise ValueError

क्या ValueError के बजाय इन्हें NotImplement किया जाना चाहिए?
spazm

5

पार्टी के लिए देर से, लेकिन मनमानी गहराई के लिए मैंने बस खुद को कुछ ऐसा करते पाया:

from collections import defaultdict

class DeepDict(defaultdict):
    def __call__(self):
        return DeepDict(self.default_factory)

यहाँ चाल मूल रूप से DeepDictउदाहरण के लिए लापता मूल्यों के निर्माण के लिए एक वैध कारखाना है। अब हम जैसे काम कर सकते हैं

dd = DeepDict(DeepDict(list))
dd[1][2].extend([3,4])
sum(dd[1][2])  # 7

ddd = DeepDict(DeepDict(DeepDict(list)))
ddd[1][2][3].extend([4,5])
sum(ddd[1][2][3])  # 9

1
def _sub_getitem(self, k):
    try:
        # sub.__class__.__bases__[0]
        real_val = self.__class__.mro()[-2].__getitem__(self, k)
        val = '' if real_val is None else real_val
    except Exception:
        val = ''
        real_val = None
    # isinstance(Avoid,dict)也是true,会一直递归死
    if type(val) in (dict, list, str, tuple):
        val = type('Avoid', (type(val),), {'__getitem__': _sub_getitem, 'pop': _sub_pop})(val)
        # 重新赋值当前字典键为返回值,当对其赋值时可回溯
        if all([real_val is not None, isinstance(self, (dict, list)), type(k) is not slice]):
            self[k] = val
    return val


def _sub_pop(self, k=-1):
    try:
        val = self.__class__.mro()[-2].pop(self, k)
        val = '' if val is None else val
    except Exception:
        val = ''
    if type(val) in (dict, list, str, tuple):
        val = type('Avoid', (type(val),), {'__getitem__': _sub_getitem, 'pop': _sub_pop})(val)
    return val


class DefaultDict(dict):
    def __getitem__(self, k):
        return _sub_getitem(self, k)

    def pop(self, k):
        return _sub_pop(self, k)

In[8]: d=DefaultDict()
In[9]: d['a']['b']['c']['d']
Out[9]: ''
In[10]: d['a']="ggggggg"
In[11]: d['a']
Out[11]: 'ggggggg'
In[12]: d['a']['pp']
Out[12]: ''

फिर कोई त्रुटि नहीं। चाहे कितने भी स्तर पर घोंसले हों। पॉप नो एरर

dd = DefaultDict ({ "1": 333333})

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