পাইথনে একাধিক স্তর 'কালেকশন.ডিফল্টিক্টিক্ট'


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
@ সায়েন্সফ্রিকশন যে নির্দিষ্ট কিছুতে আপনাকে সাহায্যের প্রয়োজন? যখন d[new_key]অ্যাক্সেস করা হয়, এটা ল্যামডা যা একটি নতুন তৈরি করবে ডাকব defaultdict(int)। এবং d[existing_key][new_key2]অ্যাক্সেস করা হলে , একটি নতুন intতৈরি করা হবে।
ইন্টারজয়

11
এটা সত্যিই দারুন. দেখে মনে হচ্ছে আমি পাইথনের প্রতি আমার বৈবাহিক মানতটি প্রতিদিনই নবায়ন করি।
এমভিসিএইচআর

3
এই পদ্ধতিটি ব্যবহার করে multiprocessingএবং একটি নামযুক্ত মডিউল-স্তরের ফাংশনটি কী সম্পর্কে আরও বিশদ অনুসন্ধান করছেন ? এই প্রশ্ন অনুসরণ করে।
সিসিলিয়া

32

বাছাইযোগ্য, নেস্টেড ডিফল্টডিক্ট্ট করার অন্য উপায় হ'ল ল্যাম্বদার পরিবর্তে আংশিক বস্তু ব্যবহার করা:

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

এটি কাজ করবে কারণ ডিফল্টডিক্টিক শ্রেণিটি বিশ্বব্যাপী মডিউল স্তরে অ্যাক্সেসযোগ্য:

"ফাংশন [বা এই ক্ষেত্রে, শ্রেণি] এটি মোড়ানো না হলে আপনি কোনও আংশিক বস্তুর আচার নিতে পারবেন না ... তার __ਨਾਮ__ এর অধীনে (তার __ মডুল__ এর মধ্যে)" - পিকিং আংশিক ক্রিয়াকলাপ


12

আরও সাধারণ সমাধানের জন্য এখানে নসক্লোর উত্তরটি দেখুন।

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}}}

@ মাইল 82 (এবং সম্পাদনা, @ যাত্রী) লিঙ্কটির জন্য ধন্যবাদ। এই পদ্ধতিটি অজগর এবং নিরাপদ?
মরলক

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

এইগুলি কি ভ্যালুআরারের চেয়ে নোটইম্প্লিমেন্টেড বাড়াতে হবে?
স্পাজম

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})

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.