নেস্টেড ডিকশনারি কার্যকর করার সর্বোত্তম উপায় কী?


201

আমার কাছে একটি ডেটা স্ট্রাকচার রয়েছে যা মূলত নেস্টেড ডিকশনারি সমান। আসুন বলি এটি দেখতে এরকম দেখাচ্ছে:

{'new jersey': {'mercer county': {'plumbers': 3,
                                  'programmers': 81},
                'middlesex county': {'programmers': 81,
                                     'salesmen': 62}},
 'new york': {'queens county': {'plumbers': 9,
                                'salesmen': 36}}}

এখন, এটি বজায় রাখা এবং তৈরি করা বেশ বেদনাদায়ক; প্রতিবার যখন আমার কাছে নতুন রাজ্য / কাউন্টি / পেশা রয়েছে তখন আমাকে নিম্ন স্তরের অভিধানগুলি অযৌক্তিক চেষ্টা / ক্যাপ ব্লকগুলির মাধ্যমে তৈরি করতে হবে। তদুপরি, আমি যদি সমস্ত মানগুলিতে যেতে চাই তবে আমাকে বিরক্তিকর নেস্টেড ইটারেটর তৈরি করতে হবে।

আমি কী হিসাবে টিপলগুলি যেমন ব্যবহার করতে পারি:

{('new jersey', 'mercer county', 'plumbers'): 3,
 ('new jersey', 'mercer county', 'programmers'): 81,
 ('new jersey', 'middlesex county', 'programmers'): 81,
 ('new jersey', 'middlesex county', 'salesmen'): 62,
 ('new york', 'queens county', 'plumbers'): 9,
 ('new york', 'queens county', 'salesmen'): 36}

এটি খুব সহজ এবং প্রাকৃতিক মানগুলির উপরে পুনরাবৃত্তি করে তোলে তবে একত্রিকরণ এবং অভিধানের উপগ্রহের দিকে তাকানোর মতো জিনিসগুলি করা আরও সিনট্যাক্টিক্যালি বেদনাদায়ক (যেমন আমি যদি কেবলমাত্র রাষ্ট্র-রাজ্যে যেতে চাই)।

মূলত, কখনও কখনও আমি কোনও নেস্টেড অভিধানকে ফ্ল্যাট অভিধান হিসাবে ভাবতে চাই এবং কখনও কখনও আমি এটিকে একটি জটিল শ্রেণিবিন্যাস হিসাবে ভাবতে চাই। আমি একটি ক্লাসে এই সমস্ত আবদ্ধ করতে পারে, তবে মনে হচ্ছে কেউ ইতিমধ্যে এটি করেছে। বিকল্পভাবে, দেখে মনে হচ্ছে এটি করার জন্য কিছু সত্যই মার্জিত সিন্টেক্সটিকাল নির্মাণ থাকতে পারে।

আমি কীভাবে এই আরও ভাল করতে পারি?

সংযোজন: আমি সচেতন setdefault()তবে এটি পরিষ্কার সংশ্লেষের জন্য আসলে তৈরি করে না। এছাড়াও, আপনার তৈরি প্রতিটি উপ-অভিধানের setdefault()ম্যানুয়ালি সেট করা দরকার।

উত্তর:


178

পাইথনে নেস্টেড অভিধানগুলি কার্যকর করার সর্বোত্তম উপায় কী?

এটি একটি খারাপ ধারণা, এটি করবেন না। পরিবর্তে, একটি নিয়মিত অভিধান ব্যবহার করুন এবং dict.setdefaultযেখানে অ্যাপ্রোপস ব্যবহার করুন , তাই যখন সাধারণ ব্যবহারের মধ্যে কীগুলি অনুপস্থিত থাকে তখন আপনি প্রত্যাশিত হন KeyError। আপনি যদি এই আচরণটি পেতে জোর দিয়ে থাকেন তবে কীভাবে নিজেকে পায়ে গুলি করতে হবে তা এখানে:

একটি নতুন উদাহরণ সেট এবং ফেরত দিতে __missing__একটি dictসাবক্লাসে প্রয়োগ করুন ।

পাইথন ২.২০ থেকে এই পদ্ধতির উপলব্ধ (এবং নথিভুক্ত) রয়েছে এবং ( এটি আমার কাছে বিশেষভাবে মূল্যবান) একটি অটিভিভিফায়েড ডিফল্টডিক্ট্টের কুরুচিপূর্ণ মুদ্রণের পরিবর্তে এটি একটি সাধারণ ডিকের মতো সুন্দরভাবে মুদ্রণ করে:

class Vividict(dict):
    def __missing__(self, key):
        value = self[key] = type(self)() # retain local pointer to value
        return value                     # faster to return than dict lookup

(নোটটি self[key]অ্যাসাইনমেন্টের বাম দিকে রয়েছে, সুতরাং এখানে কোনও পুনরাবৃত্তি নেই))

এবং বলুন যে আপনার কাছে কিছু ডেটা রয়েছে:

data = {('new jersey', 'mercer county', 'plumbers'): 3,
        ('new jersey', 'mercer county', 'programmers'): 81,
        ('new jersey', 'middlesex county', 'programmers'): 81,
        ('new jersey', 'middlesex county', 'salesmen'): 62,
        ('new york', 'queens county', 'plumbers'): 9,
        ('new york', 'queens county', 'salesmen'): 36}

আমাদের ব্যবহারের কোডটি এখানে:

vividict = Vividict()
for (state, county, occupation), number in data.items():
    vividict[state][county][occupation] = number

এবং এখন:

>>> import pprint
>>> pprint.pprint(vividict, width=40)
{'new jersey': {'mercer county': {'plumbers': 3,
                                  'programmers': 81},
                'middlesex county': {'programmers': 81,
                                     'salesmen': 62}},
 'new york': {'queens county': {'plumbers': 9,
                                'salesmen': 36}}}

সমালোচনা

এই ধরণের ধারকটির একটি সমালোচনা হ'ল যদি ব্যবহারকারী কোনও কী ভুল বানান করে থাকে তবে আমাদের কোডটি নিঃশব্দে ব্যর্থ হতে পারে:

>>> vividict['new york']['queens counyt']
{}

এবং অতিরিক্তভাবে এখন আমাদের ডেটাতে একটি ভুল বানানযুক্ত কাউন্টি থাকতে চাই:

>>> pprint.pprint(vividict, width=40)
{'new jersey': {'mercer county': {'plumbers': 3,
                                  'programmers': 81},
                'middlesex county': {'programmers': 81,
                                     'salesmen': 62}},
 'new york': {'queens county': {'plumbers': 9,
                                'salesmen': 36},
              'queens counyt': {}}}

ব্যাখ্যা:

Vividictযখনই কোনও কী অ্যাক্সেস করা হয় তবে নিখোঁজ হয় আমরা কেবল আমাদের ক্লাসের আরেকটি নেস্টেড উদাহরণ সরবরাহ করছি । (মান অ্যাসাইনমেন্টটি ফেরত দেওয়া কার্যকর কারণ এটি আমাদের অতিরিক্তভাবে ডিকের উপরে পাওয়া কলকে এড়িয়ে চলে, এবং দুর্ভাগ্যক্রমে, এটি সেট হওয়ার সাথে সাথে আমরা এটি ফিরিয়ে দিতে পারি না))

দ্রষ্টব্য, এগুলি সর্বাধিক উত্সাহিত উত্তর হিসাবে একই শব্দার্থক কিন্তু কোডের অর্ধেক লাইনে - নসক্লো এর প্রয়োগ:

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

ব্যবহারের বিক্ষোভ

নীচে কেবল কীভাবে এই ডিকটি ফ্লাইতে নেস্টেড ডিক কাঠামো তৈরি করতে ব্যবহার করা যেতে পারে তার একটি উদাহরণ দেওয়া আছে। এটি যতটা গভীরভাবে যেতে চাইবে তত তাড়াতাড়ি একটি শ্রেণিবিন্যাসের গাছ কাঠামো তৈরি করতে পারে।

import pprint

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

d = Vividict()

d['foo']['bar']
d['foo']['baz']
d['fizz']['buzz']
d['primary']['secondary']['tertiary']['quaternary']
pprint.pprint(d)

কোন ফলাফল:

{'fizz': {'buzz': {}},
 'foo': {'bar': {}, 'baz': {}},
 'primary': {'secondary': {'tertiary': {'quaternary': {}}}}}

এবং শেষ লাইনটি দেখায়, এটি সুন্দরভাবে এবং ম্যানুয়াল পরীক্ষার জন্য প্রিন্ট করে। তবে আপনি যদি নিজের ডেটাটি চাক্ষুষরূপে পরিদর্শন করতে চান তবে __missing__এর শ্রেণীর একটি নতুন উদাহরণটি কীতে সেট করে প্রয়োগ করতে প্রয়োগ করে তা আরও কার্যকর সমাধান।

অন্যান্য বিকল্প, বিপরীতে:

dict.setdefault

যদিও প্রশ্নকর্তা মনে করেন এটি পরিষ্কার নয় তবে আমি Vividictনিজের কাছে এটিই পছন্দনীয় ।

d = {} # or dict()
for (state, county, occupation), number in data.items():
    d.setdefault(state, {}).setdefault(county, {})[occupation] = number

এবং এখন:

>>> pprint.pprint(d, width=40)
{'new jersey': {'mercer county': {'plumbers': 3,
                                  'programmers': 81},
                'middlesex county': {'programmers': 81,
                                     'salesmen': 62}},
 'new york': {'queens county': {'plumbers': 9,
                                'salesmen': 36}}}

একটি ভুল বানান শোনার সাথে ব্যর্থ হবে এবং খারাপ তথ্য সহ আমাদের ডেটা বিশৃঙ্খলা করবে না:

>>> d['new york']['queens counyt']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'queens counyt'

অতিরিক্ত হিসাবে, আমি মনে করি যে লুপগুলিতে ব্যবহার করার সময় সেটডিফল্ট দুর্দান্ত কাজ করে এবং আপনি কীগুলি কী পেতে যাচ্ছেন তা আপনি জানেন না, তবে পুনরাবৃত্তিমূলক ব্যবহার বেশ বোঝা হয়ে ওঠে, এবং আমি মনে করি না যে কেউ নিম্নলিখিতগুলি বজায় রাখতে চাইবে:

d = dict()

d.setdefault('foo', {}).setdefault('bar', {})
d.setdefault('foo', {}).setdefault('baz', {})
d.setdefault('fizz', {}).setdefault('buzz', {})
d.setdefault('primary', {}).setdefault('secondary', {}).setdefault('tertiary', {}).setdefault('quaternary', {})

আরেকটি সমালোচনা হ'ল সেটডিফল্টের এটি ব্যবহৃত হয় বা না হয় তার জন্য একটি নতুন উদাহরণ প্রয়োজন। যাইহোক, পাইথন (বা কমপক্ষে সিপথন) অব্যবহৃত এবং অযৌক্তিক নতুন দৃষ্টান্তগুলি পরিচালনা করার ক্ষেত্রে স্মার্ট, উদাহরণস্বরূপ, এটি মেমরিতে অবস্থানটি পুনরায় ব্যবহার করে:

>>> id({}), id({}), id({})
(523575344, 523575344, 523575344)

একটি স্বতঃচঞ্চিত ডিফল্টডিক্ট্ট

এটি একটি ঝলকানো বাস্তবায়ন, এবং কোনও স্ক্রিপ্টে ব্যবহার যা আপনি ডেটা পরিদর্শন করছেন না তা বাস্তবায়নের মতো কার্যকর হবে __missing__:

from collections import defaultdict

def vivdict():
    return defaultdict(vivdict)

তবে আপনার যদি আপনার ডেটাটি পরীক্ষা করার প্রয়োজন হয়, একইভাবে ডেটা সহ একটি স্বতঃ-বিভুইড ডিফল্টডিক্ট্টের ফলাফলগুলি এর মতো দেখায়:

>>> d = vivdict(); d['foo']['bar']; d['foo']['baz']; d['fizz']['buzz']; d['primary']['secondary']['tertiary']['quaternary']; import pprint; 
>>> pprint.pprint(d)
defaultdict(<function vivdict at 0x17B01870>, {'foo': defaultdict(<function vivdict 
at 0x17B01870>, {'baz': defaultdict(<function vivdict at 0x17B01870>, {}), 'bar': 
defaultdict(<function vivdict at 0x17B01870>, {})}), 'primary': defaultdict(<function 
vivdict at 0x17B01870>, {'secondary': defaultdict(<function vivdict at 0x17B01870>, 
{'tertiary': defaultdict(<function vivdict at 0x17B01870>, {'quaternary': defaultdict(
<function vivdict at 0x17B01870>, {})})})}), 'fizz': defaultdict(<function vivdict at 
0x17B01870>, {'buzz': defaultdict(<function vivdict at 0x17B01870>, {})})})

এই আউটপুটটি বেশ অযৌক্তিক এবং ফলাফলগুলি বেশ অপঠনযোগ্য। সাধারণত প্রদত্ত সমাধানটি হ'ল ম্যানুয়াল ইন্সপেকশনের জন্য পুনরাবৃত্তভাবে একটি ডিকে রূপান্তর করা। এই অ-তুচ্ছ সমাধানটি পাঠকের অনুশীলন হিসাবে ছেড়ে দেওয়া হয়েছে।

কর্মক্ষমতা

পরিশেষে, আসুন কর্মক্ষমতা তাকান। আমি ইনস্ট্যান্টেশন ব্যয় বিয়োগ করছি।

>>> import timeit
>>> min(timeit.repeat(lambda: {}.setdefault('foo', {}))) - min(timeit.repeat(lambda: {}))
0.13612580299377441
>>> min(timeit.repeat(lambda: vivdict()['foo'])) - min(timeit.repeat(lambda: vivdict()))
0.2936999797821045
>>> min(timeit.repeat(lambda: Vividict()['foo'])) - min(timeit.repeat(lambda: Vividict()))
0.5354437828063965
>>> min(timeit.repeat(lambda: AutoVivification()['foo'])) - min(timeit.repeat(lambda: AutoVivification()))
2.138362169265747

পারফরম্যান্সের ভিত্তিতে, dict.setdefaultসেরা কাজ করে। আপনি যেখানে মৃত্যুদন্ড কার্যকর করার গতি সম্পর্কে যত্নশীল সে ক্ষেত্রে আমি উত্পাদন কোডের জন্য এটির সুপারিশ করব।

ইন্টারেক্টিভ ব্যবহারের জন্য যদি আপনার এটির প্রয়োজন হয় (সম্ভবত একটি আইপিথন নোটবুকে, সম্ভবত) তবে পারফরম্যান্সটি আসলে কোনও ব্যাপার নয় - এই ক্ষেত্রে, আমি আউটপুটটির পাঠযোগ্যতার জন্য ভিভিডিক্টের সাথে যাব। অটোভিভিফিকেশন অবজেক্টের তুলনায় (যা এর __getitem__পরিবর্তে ব্যবহার করে __missing__, যা এই উদ্দেশ্যে তৈরি করা হয়েছিল) এটি অনেক উচ্চতর।

উপসংহার

একটি নতুন দৃষ্টান্ত স্থাপন ও ফেরত দেওয়ার জন্য __missing__সাবক্ল্যাসডে প্রয়োগ করা dictবিকল্পের চেয়ে কিছুটা বেশি কঠিন তবে এর সুবিধা রয়েছে

  • সহজ ইনস্ট্যান্টেশন
  • সহজ ডেটা জনসংখ্যা
  • সহজ ডেটা দেখা

এবং যেহেতু এটি কম জটিল এবং সংশোধন করার চেয়ে বেশি পারফরম্যান্ট __getitem__তাই এ পদ্ধতিতে এটি পছন্দ করা উচিত।

তবুও, এর ঘাটতি রয়েছে:

  • খারাপ অনুসন্ধানগুলি নীরবে ব্যর্থ হবে।
  • খারাপ চেহারা অভিধানে থাকবে।

সুতরাং আমি ব্যক্তিগতভাবে setdefaultঅন্যান্য সমাধানগুলিতে পছন্দ করি এবং প্রতিটি পরিস্থিতিতে যেখানে আমার এই ধরণের আচরণের প্রয়োজন হয়েছে have


দুর্দান্ত উত্তর! এর জন্য একটি সীমাবদ্ধ গভীরতা এবং একটি পাতার ধরণ নির্দিষ্ট করার কোনও উপায় আছে কি Vividict? উদাহরণস্বরূপ 3এবং listতালিকার ডিক্ট এর ডিকের জন্য যা জনসংখ্যাযুক্ত হতে পারে d['primary']['secondary']['tertiary'].append(element)। আমি প্রতিটি গভীরতার জন্য 3 টি পৃথক ক্লাস সংজ্ঞায়িত করতে পারি তবে আমি একটি ক্লিনার সমাধান খুঁজে পেতে চাই।
এরিক ডুমিনিল

@ এরিকডুমিনিল d['primary']['secondary'].setdefault('tertiary', []).append('element')- ?? প্রশংসা করার জন্য ধন্যবাদ, তবে আমাকে সৎ হতে দিন - আমি আসলে কখনই ব্যবহার করি না __missing__- আমি সর্বদা ব্যবহার করি setdefault। আমি সম্ভবত আমার উপসংহার / ইন্ট্রো আপডেট করা উচিত ...
হারুন হলের

@ অ্যারোনহল সঠিক আচরণ হ'ল কোডটি প্রয়োজনে একটি ডিক তৈরি করা উচিত। এই ক্ষেত্রে পূর্ববর্তী নির্ধারিত মানটি ওভাররাইড করে।
নেহেম

@ অ্যারনহল এছাড়াও The bad lookup will remain in the dictionary.আমি কী এই সমাধানটি ব্যবহার করার বিষয়ে বিবেচনা করছি তার অর্থ কী তা বুঝতে আপনি আমাকে সহায়তা করতে পারেন ? অনেক প্রশংসিত. ধন্যবা
nehem

@ অ্যারোনহল এটি setdefaultযখন সমস্যাটি দুই স্তরের গভীরতার অধীনে বাসা বেঁধেছে তখন তা ব্যর্থ হবে। দেখে মনে হচ্ছে পাইথনের কোনও কাঠামো বর্ণিত হিসাবে সত্য বিচক্ষণকরণ সরবরাহ করতে পারে না। আমাকে দুটি স্টেটিং পদ্ধতির জন্য নিষ্পত্তি get_nestedকরতে হয়েছিল set_nestedযার একটি হল & যার জন্য ডিকের জন্য একটি রেফারেন্স এবং নেস্টেড গুণাবলীর তালিকা গ্রহণ করুন।
নেহেম

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

অজগর 3.x এ চলে গেলে কারও কি এই সমস্যা হয়? stackoverflow.com/questions/54622935/…
জেসন

@ জেসন pickleঅজগর সংস্করণের মধ্যে ভয়ঙ্কর। আপনি যে ডেটা রাখতে চান তা সঞ্চয় করতে এটি ব্যবহার করা এড়িয়ে চলুন। এটি কেবল ক্যাশে এবং স্টাফের জন্য ব্যবহার করুন যা আপনি ইচ্ছামতো ডাম্প এবং পুনঃজেনার করতে পারেন। দীর্ঘমেয়াদী সঞ্চয়স্থান বা সিরিয়ালাইজেশন পদ্ধতি হিসাবে নয়।
nosklo

এই জিনিসগুলি সঞ্চয় করতে আপনি কী ব্যবহার করেন? আমার অটোভিভিফিকেশন অবজেক্টে কেবল পান্ডাস ডেটা ফ্রেম এবং স্ট্রিং রয়েছে।
জেসন

@ জেসন ডেটার উপর নির্ভর করে, আমি এটি সংরক্ষণ করতে জেএসএন, সিএসভি ফাইল, এমনকি একটি sqliteডাটাবেস ব্যবহার করতে চাই ।
নসক্লো

30

আমি এই ছোটটি একটিও দেখিনি, এখানে একটি ডিক রয়েছে যা আপনার পছন্দ মতো নেস্টেড হয়ে যায়, কোনও ঘাম নয়:

# yo dawg, i heard you liked dicts                                                                      
def yodict():
    return defaultdict(yodict)

2
@ উবেরি: আসলে আপনার যা প্রয়োজন তা হল yodict = lambda: defaultdict(yodict)
মার্টিনো

1
স্বীকৃত সংস্করণ একটি সাবক্লাস dict, তাই সম্পূর্ণ সমতুল্য x = Vdict(a=1, b=2)হতে আমাদের কাজ করা প্রয়োজন ।
ওয়াবেরি

@ ওয়াবেরি: গ্রহণযোগ্য উত্তরে যা-ই হোক না কেন, dictওপি কর্তৃক বলা একটি সাবক্লাস হওয়া প্রয়োজন ছিল না, যারা কেবলমাত্র তাদের বাস্তবায়নের জন্য "সেরা উপায়" চেয়েছিলেন - এবং তা ছাড়া, এটি করা উচিত / উচিত নয় পাইথনে যাই হোক না কেন ব্যাপার।
মার্টিনিউ

24

আপনি একটি ওয়াইএমএল ফাইল তৈরি করতে পারেন এবং পাইওয়ামিল ব্যবহার করে এটি পড়তে পারেন ।

পদক্ষেপ 1: "এমপ্লয়মেন্ট.আইএমএল" একটি ওয়াইএএমএল ফাইল তৈরি করুন:

new jersey:
  mercer county:
    pumbers: 3
    programmers: 81
  middlesex county:
    salesmen: 62
    programmers: 81
new york:
  queens county:
    plumbers: 9
    salesmen: 36

পদক্ষেপ 2: পাইথনে এটি পড়ুন

import yaml
file_handle = open("employment.yml")
my_shnazzy_dictionary = yaml.safe_load(file_handle)
file_handle.close()

এবং এখন my_shnazzy_dictionaryআপনার সমস্ত মান আছে। আপনার যদি ফ্লাইতে এটি করার দরকার হয় তবে আপনি ওয়াইএএমএলটিকে স্ট্রিং হিসাবে তৈরি করতে এবং এতে ফিড দিতে পারেন yaml.safe_load(...)


4
প্রচুর গভীরভাবে নেস্টেড ডেটা (এবং কনফিগারেশন ফাইল, ডেটাবেস মকআপস, ইত্যাদি ...) ইনপুট করার জন্য ওয়াইএএমএল অবশ্যই আমার পছন্দ। যদি ওপি আশেপাশের অতিরিক্ত ফাইলগুলি না চায়, কেবল কিছু ফাইলে নিয়মিত পাইথন স্ট্রিং ব্যবহার করুন এবং ওয়াইএএমএল দিয়ে পার্স করুন।
kmelvn

ওয়াইএএমএল স্ট্রিংগুলি তৈরি করার ভাল বিষয়: বার বার "টেম্পাইল ফাইল" মডিউলটি ব্যবহার করার চেয়ে এটি অনেক পরিষ্কার উপায়।
পিট

18

যেহেতু আপনার কাছে স্টার-স্কিমা ডিজাইন রয়েছে, আপনি এটি আরও একটি সম্পর্কিত টেবিলের মতো এবং অভিধানের মতো কম কাঠামো তৈরি করতে চাইতে পারেন।

import collections

class Jobs( object ):
    def __init__( self, state, county, title, count ):
        self.state= state
        self.count= county
        self.title= title
        self.count= count

facts = [
    Jobs( 'new jersey', 'mercer county', 'plumbers', 3 ),
    ...

def groupBy( facts, name ):
    total= collections.defaultdict( int )
    for f in facts:
        key= getattr( f, name )
        total[key] += f.count

এই জাতীয় জিনিসটি এসকিউএল ওভারহেড ছাড়াই ডেটা গুদামের মতো নকশা তৈরি করতে দীর্ঘতর পথ যেতে পারে।


14

যদি বাসা বাঁধার মাত্রা কম থাকে তবে আমি এটির collections.defaultdictজন্য ব্যবহার করি :

from collections import defaultdict

def nested_dict_factory(): 
  return defaultdict(int)
def nested_dict_factory2(): 
  return defaultdict(nested_dict_factory)
db = defaultdict(nested_dict_factory2)

db['new jersey']['mercer county']['plumbers'] = 3
db['new jersey']['mercer county']['programmers'] = 81

ব্যবহার defaultdictনোংরা অনেকটা এই রকম এড়াতে setdefault(), get()ইত্যাদি


+1: পাইথনে আমার সর্বকালের প্রিয় সংযোজনগুলির মধ্যে ডিফল্টডিক্ট্ট। আর .setdefault ()!
জন ফুহি

8

এটি এমন একটি ফাংশন যা স্বেচ্ছাসেবী গভীরতার নেস্টেড ডিকশনারিটি দেয়:

from collections import defaultdict
def make_dict():
    return defaultdict(make_dict)

এটি এর মতো ব্যবহার করুন:

d=defaultdict(make_dict)
d["food"]["meat"]="beef"
d["food"]["veggie"]="corn"
d["food"]["sweets"]="ice cream"
d["animal"]["pet"]["dog"]="collie"
d["animal"]["pet"]["cat"]="tabby"
d["animal"]["farm animal"]="chicken"

এই জাতীয় কিছু দিয়ে সমস্ত কিছু দিয়ে ইটারেট করুন:

def iter_all(d,depth=1):
    for k,v in d.iteritems():
        print "-"*depth,k
        if type(v) is defaultdict:
            iter_all(v,depth+1)
        else:
            print "-"*(depth+1),v

iter_all(d)

এটি মুদ্রণ করে:

- food
-- sweets
--- ice cream
-- meat
--- beef
-- veggie
--- corn
- animal
-- pet
--- dog
---- labrador
--- cat
---- tabby
-- farm animal
--- chicken

আপনি শেষ পর্যন্ত এটি তৈরি করতে চাইতে পারেন যাতে ডেকে নতুন আইটেম যুক্ত করা যায় না। এগুলি পুনরাবৃত্তভাবে defaultdictসাধারণ এসগুলিতে রূপান্তর করা সহজ dict

def dictify(d):
    for k,v in d.iteritems():
        if isinstance(v,defaultdict):
            d[k] = dictify(v)
    return dict(d)

7

আমি setdefaultবেশ দরকারী বলে মনে করি; এটি কোনও কী উপস্থিত রয়েছে কিনা তা পরীক্ষা করে এবং এটি যোগ না করে যুক্ত করে:

d = {}
d.setdefault('new jersey', {}).setdefault('mercer county', {})['plumbers'] = 3

setdefault সর্বদা প্রাসঙ্গিক কী প্রদান করে, তাই আপনি আসলে 'এর মানগুলি আপডেট করছেনd জায়গায় ' এর ।

এটি পুনরাবৃত্তির ক্ষেত্রে, আমি নিশ্চিত যে পাইথনের মধ্যে ইতিমধ্যে কোনও উপস্থিত না থাকলে আপনি সহজেই যথেষ্ট পরিমাণে জেনারেটর লিখতে পারেন:

def iterateStates(d):
    # Let's count up the total number of "plumbers" / "dentists" / etc.
    # across all counties and states
    job_totals = {}

    # I guess this is the annoying nested stuff you were talking about?
    for (state, counties) in d.iteritems():
        for (county, jobs) in counties.iteritems():
            for (job, num) in jobs.iteritems():
                # If job isn't already in job_totals, default it to zero
                job_totals[job] = job_totals.get(job, 0) + num

    # Now return an iterator of (job, number) tuples
    return job_totals.iteritems()

# Display all jobs
for (job, num) in iterateStates(d):
    print "There are %d %s in total" % (job, num)

আমি এই সমাধান মত কিন্তু যখন আমি চেষ্টা করে দেখুন: count.setdefault (ক, {}) setdefault (খ, {}) setdefault (C, 0) + + = 1 আমি "উদ্দীপ্ত অ্যাসাইনমেন্টের জন্য অবৈধ অভিব্যক্তি"
dfrankow

6

অন্যরা যেমন পরামর্শ দিয়েছে, একটি রিলেশনাল ডাটাবেস আপনার পক্ষে আরও কার্যকর হতে পারে। আপনি সারণী তৈরি করতে এবং তারপরে কোয়েরি করতে একটি ডেটা কাঠামো হিসাবে ইন-মেমোরি স্ক্লাইট 3 ডাটাবেস ব্যবহার করতে পারেন।

import sqlite3

c = sqlite3.Connection(':memory:')
c.execute('CREATE TABLE jobs (state, county, title, count)')

c.executemany('insert into jobs values (?, ?, ?, ?)', [
    ('New Jersey', 'Mercer County',    'Programmers', 81),
    ('New Jersey', 'Mercer County',    'Plumbers',     3),
    ('New Jersey', 'Middlesex County', 'Programmers', 81),
    ('New Jersey', 'Middlesex County', 'Salesmen',    62),
    ('New York',   'Queens County',    'Salesmen',    36),
    ('New York',   'Queens County',    'Plumbers',     9),
])

# some example queries
print list(c.execute('SELECT * FROM jobs WHERE county = "Queens County"'))
print list(c.execute('SELECT SUM(count) FROM jobs WHERE title = "Programmers"'))

এটি কেবল একটি সাধারণ উদাহরণ। আপনি রাজ্য, কাউন্টি এবং কাজের শিরোনামের জন্য পৃথক সারণী সংজ্ঞায়িত করতে পারেন।


5

collections.defaultdictনেস্টেড ডিক তৈরি করতে উপ-শ্রেণিবদ্ধ হতে পারে। তারপরে সেই শ্রেণিতে কোনও কার্যকর পুনরাবৃত্তি পদ্ধতি যুক্ত করুন।

>>> from collections import defaultdict
>>> class nesteddict(defaultdict):
    def __init__(self):
        defaultdict.__init__(self, nesteddict)
    def walk(self):
        for key, value in self.iteritems():
            if isinstance(value, nesteddict):
                for tup in value.walk():
                    yield (key,) + tup
            else:
                yield key, value


>>> nd = nesteddict()
>>> nd['new jersey']['mercer county']['plumbers'] = 3
>>> nd['new jersey']['mercer county']['programmers'] = 81
>>> nd['new jersey']['middlesex county']['programmers'] = 81
>>> nd['new jersey']['middlesex county']['salesmen'] = 62
>>> nd['new york']['queens county']['plumbers'] = 9
>>> nd['new york']['queens county']['salesmen'] = 36
>>> for tup in nd.walk():
    print tup


('new jersey', 'mercer county', 'programmers', 81)
('new jersey', 'mercer county', 'plumbers', 3)
('new jersey', 'middlesex county', 'programmers', 81)
('new jersey', 'middlesex county', 'salesmen', 62)
('new york', 'queens county', 'salesmen', 36)
('new york', 'queens county', 'plumbers', 9)

1
এটিই উত্তর যা আমি যা খুঁজছিলাম তার কাছাকাছি আসে। তবে আদর্শভাবে এখানে সমস্ত ধরণের সহায়ক ফাংশন থাকবে, যেমন: ওয়াক_কিজ () বা এ জাতীয়। আমি অবাক হয়েছি স্ট্যান্ডার্ড লাইব্রেরিতে এটি করার মতো কিছুই নেই।
ওয়াইজিএ

4

যেমন "অসৎ চেষ্টা / ব্লক ধরুন":

d = {}
d.setdefault('key',{}).setdefault('inner key',{})['inner inner key'] = 'value'
print d

উৎপাদনের

{'key': {'inner key': {'inner inner key': 'value'}}}

আপনি এটি আপনার ফ্ল্যাট অভিধানের বিন্যাস থেকে কাঠামোগত বিন্যাসে রূপান্তর করতে ব্যবহার করতে পারেন:

fd = {('new jersey', 'mercer county', 'plumbers'): 3,
 ('new jersey', 'mercer county', 'programmers'): 81,
 ('new jersey', 'middlesex county', 'programmers'): 81,
 ('new jersey', 'middlesex county', 'salesmen'): 62,
 ('new york', 'queens county', 'plumbers'): 9,
 ('new york', 'queens county', 'salesmen'): 36}

for (k1,k2,k3), v in fd.iteritems():
    d.setdefault(k1, {}).setdefault(k2, {})[k3] = v

4

আপনি আসক্তি ব্যবহার করতে পারেন: https://github.com/mewwts/addict

>>> from addict import Dict
>>> my_new_shiny_dict = Dict()
>>> my_new_shiny_dict.a.b.c.d.e = 2
>>> my_new_shiny_dict
{'a': {'b': {'c': {'d': {'e': 2}}}}}

4

defaultdict() তোমার বন্ধু!

দ্বিমাত্রিক অভিধানের জন্য আপনি এটি করতে পারেন:

d = defaultdict(defaultdict)
d[1][2] = 3

আরও মাত্রা জন্য আপনি করতে পারেন:

d = defaultdict(lambda :defaultdict(defaultdict))
d[1][2][3] = 4

এই উত্তরটি সর্বোত্তমভাবে মাত্র তিনটি স্তরের জন্য কাজ করে। স্বেচ্ছাচারী স্তরের জন্য, এই উত্তরটি বিবেচনা করুন ।
একিউম্যানাস

3

আপনার নেস্টেড ডিকশনারিটি দিয়ে সহজে পুনরাবৃত্তি করার জন্য, কেন কেবল সাধারণ জেনারেটরটি লিখছেন না?

def each_job(my_dict):
    for state, a in my_dict.items():
        for county, b in a.items():
            for job, value in b.items():
                yield {
                    'state'  : state,
                    'county' : county,
                    'job'    : job,
                    'value'  : value
                }

সুতরাং, আপনার কাছে যদি আপনার সংকলিত নেস্টেট ডিকশনারি থাকে তবে এর উপর পুনরাবৃত্তি করা সহজ হয়ে যায়:

for r in each_job(my_dict):
    print "There are %d %s in %s, %s" % (r['value'], r['job'], r['county'], r['state'])

স্পষ্টতই আপনার জেনারেটর যে কোনও ডেটা ফর্ম্যাট আপনার জন্য দরকারী ফলন করতে পারে।

আপনি গাছটি পড়ার জন্য কেন ক্যাচ ব্লক ব্যবহার করছেন? এটি কী পুনরুদ্ধার করার আগে ডিকের মধ্যে কী উপস্থিত রয়েছে কিনা তা অনুসন্ধান করা যথেষ্ট সহজ (এবং সম্ভবত নিরাপদ)। গার্ড ক্লজগুলি ব্যবহার করে একটি ফাংশন এটির মতো দেখতে পারে:

if not my_dict.has_key('new jersey'):
    return False

nj_dict = my_dict['new jersey']
...

অথবা, সম্ভবত কিছুটা ভার্বোজ পদ্ধতি হ'ল get পদ্ধতিটি ব্যবহার করা:

value = my_dict.get('new jersey', {}).get('middlesex county', {}).get('salesmen', 0)

তবে কিছুটা সংক্ষিপ্ত পথের জন্য আপনি একটি সংগ্রহগুলি ব্যবহার করতে পারেন mightডফাল্টিক্টিক্ট , যা পাইথন 2.5 এর পরে স্ট্যান্ডার্ড লাইব্রেরির অংশ।

import collections

def state_struct(): return collections.defaultdict(county_struct)
def county_struct(): return collections.defaultdict(job_struct)
def job_struct(): return 0

my_dict = collections.defaultdict(state_struct)

print my_dict['new jersey']['middlesex county']['salesmen']

আমি এখানে আপনার ডেটা কাঠামোর অর্থ সম্পর্কে অনুমান করছি, তবে আপনি আসলে যা করতে চান তার জন্য সামঞ্জস্য করা সহজ হওয়া উচিত।


2

এটিকে কোনও শ্রেণিতে মোড়ানো এবং বাস্তবায়ন করার ধারণাটি আমি পছন্দ করি __getitem__এবং __setitem__এগুলি যে তারা একটি সাধারণ ক্যোয়ারী ভাষা প্রয়োগ করেছে:

>>> d['new jersey/mercer county/plumbers'] = 3
>>> d['new jersey/mercer county/programmers'] = 81
>>> d['new jersey/mercer county/programmers']
81
>>> d['new jersey/mercer country']
<view which implicitly adds 'new jersey/mercer county' to queries/mutations>

আপনি অভিনবতা পেতে চাইলে আপনি এর মতো কিছু প্রয়োগ করতেও পারেন:

>>> d['*/*/programmers']
<view which would contain 'programmers' entries>

তবে বেশিরভাগই আমি মনে করি যে এটি বাস্তবায়িত করতে মজার জিনিস হবে: ডি


আমি মনে করি এটি একটি খারাপ ধারণা - আপনি কখনই কীগুলির সিনট্যাক্সটি অনুমান করতে পারবেন না। আপনি এখনও গেটিটেম এবং সেটাইটেমকে ওভাররাইড করতে পারেন তবে তাদের টিপলস নিতে পারেন।
YGA

3
@ ওয়াইজিএ আপনি সম্ভবত সঠিক, তবে এই জাতীয় ভাষা প্রয়োগের কথা ভাবা মজাদার।
হারুন মেনপা

1

আপনার ডেটাসেটটি খুব ছোট থাকতে না পারলে আপনি একটি সম্পর্কিত ডেটাবেস ব্যবহার করে বিবেচনা করতে পারেন। এটি আপনি যা চান ঠিক তা করবে: গণনা যুক্ত করা, গণনার উপ-উপকরণ নির্বাচন করা, এমনকি রাষ্ট্র, কাউন্টি, পেশা বা এগুলির কোনও সংমিশ্রণ দ্বারা সামগ্রিক গণনাগুলিও সহজ করে তোলে।


1
class JobDb(object):
    def __init__(self):
        self.data = []
        self.all = set()
        self.free = []
        self.index1 = {}
        self.index2 = {}
        self.index3 = {}

    def _indices(self,(key1,key2,key3)):
        indices = self.all.copy()
        wild = False
        for index,key in ((self.index1,key1),(self.index2,key2),
                                             (self.index3,key3)):
            if key is not None:
                indices &= index.setdefault(key,set())
            else:
                wild = True
        return indices, wild

    def __getitem__(self,key):
        indices, wild = self._indices(key)
        if wild:
            return dict(self.data[i] for i in indices)
        else:
            values = [self.data[i][-1] for i in indices]
            if values:
                return values[0]

    def __setitem__(self,key,value):
        indices, wild = self._indices(key)
        if indices:
            for i in indices:
                self.data[i] = key,value
        elif wild:
            raise KeyError(k)
        else:
            if self.free:
                index = self.free.pop(0)
                self.data[index] = key,value
            else:
                index = len(self.data)
                self.data.append((key,value))
                self.all.add(index)
            self.index1.setdefault(key[0],set()).add(index)
            self.index2.setdefault(key[1],set()).add(index)
            self.index3.setdefault(key[2],set()).add(index)

    def __delitem__(self,key):
        indices,wild = self._indices(key)
        if not indices:
            raise KeyError
        self.index1[key[0]] -= indices
        self.index2[key[1]] -= indices
        self.index3[key[2]] -= indices
        self.all -= indices
        for i in indices:
            self.data[i] = None
        self.free.extend(indices)

    def __len__(self):
        return len(self.all)

    def __iter__(self):
        for key,value in self.data:
            yield key

উদাহরণ:

>>> db = JobDb()
>>> db['new jersey', 'mercer county', 'plumbers'] = 3
>>> db['new jersey', 'mercer county', 'programmers'] = 81
>>> db['new jersey', 'middlesex county', 'programmers'] = 81
>>> db['new jersey', 'middlesex county', 'salesmen'] = 62
>>> db['new york', 'queens county', 'plumbers'] = 9
>>> db['new york', 'queens county', 'salesmen'] = 36

>>> db['new york', None, None]
{('new york', 'queens county', 'plumbers'): 9,
 ('new york', 'queens county', 'salesmen'): 36}

>>> db[None, None, 'plumbers']
{('new jersey', 'mercer county', 'plumbers'): 3,
 ('new york', 'queens county', 'plumbers'): 9}

>>> db['new jersey', 'mercer county', None]
{('new jersey', 'mercer county', 'plumbers'): 3,
 ('new jersey', 'mercer county', 'programmers'): 81}

>>> db['new jersey', 'middlesex county', 'programmers']
81

>>>

সম্পাদনা করুন: ওয়াইল্ড কার্ড ( None) এবং কোথাও একক মান দিয়ে জিজ্ঞাসা করার সময় এখন অভিধানে ফিরতি ।


ফিরতি তালিকা কেন? মনে হয় এটি একটি অভিধান (যাতে প্রতিটি সংখ্যাটি কী উপস্থাপন করে তা আপনি জানেন) বা একটি পরিমাণ (যেহেতু তালিকার সাথে আপনি যা করতে পারেন তা সত্যিই তাই) should
বেন ফাঁকা

0

আমি একই জিনিস চলছে। আমার অনেকগুলি মামলা রয়েছে যেখানে আমি করি:

thedict = {}
for item in ('foo', 'bar', 'baz'):
  mydict = thedict.get(item, {})
  mydict = get_value_for(item)
  thedict[item] = mydict

কিন্তু অনেক স্তরের গভীরে যাচ্ছে। এটি ".get (আইটেম,}}") "এটিই মূল কী কারণ এটি যদি ইতিমধ্যে কোনও শব্দ না থাকে তবে এটি অন্য অভিধান তৈরি করে। এদিকে, আমি এটিকে আরও ভালভাবে মোকাবিলা করার উপায়গুলি নিয়ে ভাবছিলাম। এই মুহূর্তে, অনেক আছে

value = mydict.get('foo', {}).get('bar', {}).get('baz', 0)

সুতরাং পরিবর্তে, আমি তৈরি করেছি:

def dictgetter(thedict, default, *args):
  totalargs = len(args)
  for i,arg in enumerate(args):
    if i+1 == totalargs:
      thedict = thedict.get(arg, default)
    else:
      thedict = thedict.get(arg, {})
  return thedict

আপনি যদি এটি করেন তবে একই প্রভাব ফেলবে:

value = dictgetter(mydict, 0, 'foo', 'bar', 'baz')

উত্তম? আমি তাই মনে করি.


0

আপনি ল্যাম্বডাস এবং ডিফল্টডিক্টে পুনরাবৃত্তি ব্যবহার করতে পারেন, নাম নির্ধারণ করার দরকার নেই:

a = defaultdict((lambda f: f(f))(lambda g: lambda:defaultdict(g(g))))

এখানে একটি উদাহরণ:

>>> a['new jersey']['mercer county']['plumbers']=3
>>> a['new jersey']['middlesex county']['programmers']=81
>>> a['new jersey']['mercer county']['programmers']=81
>>> a['new jersey']['middlesex county']['salesmen']=62
>>> a
defaultdict(<function __main__.<lambda>>,
        {'new jersey': defaultdict(<function __main__.<lambda>>,
                     {'mercer county': defaultdict(<function __main__.<lambda>>,
                                  {'plumbers': 3, 'programmers': 81}),
                      'middlesex county': defaultdict(<function __main__.<lambda>>,
                                  {'programmers': 81, 'salesmen': 62})})})

0

আমি এই ফাংশনটি ব্যবহার করতাম। এটি নিরাপদ, দ্রুত, সহজেই রক্ষণাবেক্ষণযোগ্য।

def deep_get(dictionary, keys, default=None):
    return reduce(lambda d, key: d.get(key, default) if isinstance(d, dict) else default, keys.split("."), dictionary)

উদাহরণ:

>>> from functools import reduce
>>> def deep_get(dictionary, keys, default=None):
...     return reduce(lambda d, key: d.get(key, default) if isinstance(d, dict) else default, keys.split("."), dictionary)
...
>>> person = {'person':{'name':{'first':'John'}}}
>>> print (deep_get(person, "person.name.first"))
John
>>> print (deep_get(person, "person.name.lastname"))
None
>>> print (deep_get(person, "person.name.lastname", default="No lastname"))
No lastname
>>>
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.