পাইথনে নেস্টেড অভিধানগুলি কার্যকর করার সর্বোত্তম উপায় কী?
এটি একটি খারাপ ধারণা, এটি করবেন না। পরিবর্তে, একটি নিয়মিত অভিধান ব্যবহার করুন এবং 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 টি পৃথক ক্লাস সংজ্ঞায়িত করতে পারি তবে আমি একটি ক্লিনার সমাধান খুঁজে পেতে চাই।