کلاس dict در Python متدهای مفید زیادی برای کار با دیکشنریها در اختیار شما میگذارد که برخی از آنها در مفهوم dicts معرفی شدند.
در اینجا به چند مورد دیگر میپردازیم، همراه با چند تکنیک برای پیمایش و دستکاری دیکشنریها.
setdefault() برای درج بدون خطادر مفهوم دیکشنری پیشتر گفته شد که .get(key, <default value>) یک value موجود را برمیگرداند، یا اگر key در دیکشنری پیدا نشود، default value را برمیگرداند و به این ترتیب از وقوع KeyError جلوگیری میکند.
این روش در موقعیتهایی خوب کار میکند که ترجیح میدهید مدیریت خطای اضافی نداشته باشید، اما نمیتوانید مطمئن باشید که key مورد جستوجو وجود دارد.
برای یک عملیات درج به همان اندازه «ایمن» (بدون KeyError)، متد .setdefault(key, <default value>) وجود دارد.
اگر key در دیکشنری پیدا شود، setdefault(key, <default value>) مقدار value را برمیگرداند.
اگر کلید پیدا نشود، جفت (key، default value) را درج میکند و default value را برای استفاده برمیگرداند.
>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
# Looking for the value associated with key "Rock Brown".
# The key does not exist, so it is added with the default value, and the value is returned.
>>> palette_I.setdefault('Rock Brown', '#694605')
'#694605'
# The (key, default value) pair has now been added to the dictionary.
>>> palette_I
{'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 'Rock Brown': '#694605'}
fromkeys() برای پر کردن دیکشنری از یک iterableبرای پر کردن سریع یک دیکشنری با keys و مقادیر پیشفرض مختلف، متد کلاس fromkeys(iterable, <default value>) روی یک iterable از keys پیمایش میکند و یک dict جدید میسازد.
همهی values روی default value دادهشده تنظیم میشوند:
>>> new_dict = dict.fromkeys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'], 'fill in hex color here')
{'Grassy Green': 'fill in hex color here',
'Purple Mountains Majesty': 'fill in hex color here',
'Misty Mountain Pink': 'fill in hex color here'}
متدهای .keys()، .values() و .items() viewهای قابل پیمایش یک دیکشنری را برمیگردانند.
با این viewها میتوان بهراحتی روی ورودیها حلقه زد، بدون آنکه آنها را تغییر داد.
viewها همچنین پویا هستند: وقتی دادههای زیرین دیکشنری تغییر میکنند، view object مرتبط نیز آن تغییر را نشان میدهد:
>>> palette_I = {'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd'}
# Using .keys() returns a list of keys.
>>> palette_I.keys()
dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'])
# Using .values() returns a list of values.
>>> palette_I.values()
dict_values(['#9bc400', '#8076a3', '#f9c5bd'])
# Using .items() returns a list of (key, value) tuples.
>>> palette_I.items()
dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', '#8076a3'), ('Misty Mountain Pink', '#f9c5bd')])
# Views are dynamic. Changing values in the dict changes all of the associated views.
>>> palette_I['Purple Mountains Majesty'] = (128, 118, 163)
>>> palette_I['Deep Red'] = '#932432'
>>> palette_I.values()
dict_values(['#9bc400', (128, 118, 163), '#f9c5bd', '#932432'])
>>> palette_I.keys()
dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink', 'Deep Red'])
>>> palette_I.items()
dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', (128, 118, 163)), ('Misty Mountain Pink', '#f9c5bd'), ('Deep Red', '#932432')])
.keys()، .values() و .items()
در Python ۳.۷ و بالاتر، dicts ترتیب درج ورودیها را حفظ میکنند و به این ترتیب هنگام استفاده از .keys()، .values() یا .items() پیمایش بهصورت اولینورودیاولخروجی (FIFO) امکانپذیر است.
در Python ۳.۸ و بالاتر، viewها همچنین برگشتپذیر هستند.
این امکان را میدهد که کلیدها، مقادیر، یا جفتهای (key، value) با استفاده از reversed(<dict>.keys())، reversed(<dict>.values()) یا reversed(<dict>.items()) به ترتیب آخرینورودیاولخروجی (LIFO) پیمایش شوند:
>>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
# Iterating in insertion order (First in, first out)
>>> for item in palette_II.items():
... print(item)
...
('Factory Stone Purple', '#7c677f')
('Green Treeline', '#478559')
('Purple baseline', '#161748')
# Iterating in the reverse direction. (Last in, first out)
>>> for item in reversed(palette_II.items()):
... print (item)
...
('Purple baseline', '#161748')
('Green Treeline', '#478559')
('Factory Stone Purple', '#7c677f')
دیکشنریها متد مرتبسازی درونساخت ندارند.
با این حال میتوان یک view از dict را با استفاده از تابع درونساخت sorted() به همراه dict.items() مرتب کرد.
سپس میتوان از view مرتبشده برای ساختن یک دیکشنری جدید استفاده کرد.
مانند پیمایش، مرتبسازی پیشفرض بر اساس keys دیکشنری انجام میشود.
# Default ordering for a dictionary is insertion order (First in, first out).
>>> color_palette = {'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd',
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple baseline': '#161748'}
# The default sort order for a dictionary uses the keys.
>>> sorted_palette = dict(sorted(color_palette.items()))
>>> sorted_palette
{'Factory Stone Purple': '#7c677f',
'Grassy Green': '#9bc400',
'Green Treeline': '#478559',
'Misty Mountain Pink': '#f9c5bd',
'Purple Mountains Majesty': '#8076a3',
'Purple baseline': '#161748'}
.update()
از <dict_one>.update(<dict_two>) میتوان برای ترکیب دو دیکشنری استفاده کرد.
این متد جفتهای (key,value) موجود در <dict_two> را میگیرد و آنها را در <dict_one> مینویسد:
>>> palette_I = {'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd'}
>>> palette_II = {'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple Baseline': '#161748'}
>>> palette_I.update(palette_II)
# Note that new items from palette_II are added.
>>> palette_I
{'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple Baseline': '#161748'}
در جاهایی که کلیدهای دو دیکشنری همپوشانی دارند، value موجود در dict_one با value متناظر از dict_two بازنویسی میشود:
>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd',
'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
>>> palette_III = {'Grassy Green': (155, 196, 0), 'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189)}
>>> palette_I.update(palette_III)
# Overlapping values in palette_I are replaced with values from palette_III
>>> palette_I
{'Grassy Green': (155, 196, 0),
'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189),
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559', 'Purple baseline': '#161748'}
| و |=)Python ۳.۹ روش متفاوتی برای ادغام dicts معرفی میکند: عملگرهای union.
dict_one | dict_two یک دیکشنری جدید میسازد که از جفتهای (key، value) موجود در dict_one و dict_two تشکیل شده است.
وقتی هر دو دیکشنری کلیدهای مشترکی داشته باشند، مقادیر dict_two اولویت دارند.
>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
>>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
>>> new_dict = palette_I | palette_II
>>> new_dict
...
{'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd',
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple baseline': '#161748'}
dict_one |= other رفتاری مشابه <dict_one>.update(<other>) دارد، اما در این حالت other میتواند یک dict یا یک iterable از جفتهای (key، value) باشد:
>>> palette_III = {'Grassy Green': (155, 196, 0),
'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189)}
>>> new_dict |= palette_III
>>> new_dict
...
{'Grassy Green': (155, 196, 0),
'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189),
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple baseline': '#161748'}
برای توضیح مفصل دیکشنریها و متدهای کار با آنها، آموزش رسمی و مرجع رسمی کتابخانه نقطههای شروع عالیای هستند.
Real Python و Finxter نیز مقالههای بسیار کاملی دربارهی دیکشنریهای Python دارند.
مکا مانچ™، شرکتی در حوزهی خودکارسازی خرید مواد غذایی، همین حالا شما را برای کار روی اپلیکیشن سفارشگیریاش استخدام کرده است. وظیفهی تیم شما ساخت یک MVP (حداقل محصول پذیرفتنی) است که همهی فعالیتهای پایهی سبد خرید را مدیریت کند و به کاربران امکان دهد سفارشهای مواد غذاییشان را اضافه، حذف و مرتب کنند. خوشبختانه، تیم دیگری مسئول همهی کارهای مربوط به پول و تسویهحساب است!
این MVP باید به کاربر اجازه دهد عنصرهایی به سبد خریدش اضافه کند. این میتواند یک عنصر یا چند عنصر بهصورت همزمان باشد. از آنجا که این یک MVP است، مقدار هر عنصر با تکرار نشان داده میشود. اگر کاربر بخواهد ۲ پرتقال اضافه کند، «Oranges» دو بار در تکرارپذیر ورودی ظاهر میشود. اگر کاربر از قبل آن عنصر را در سبدش داشته باشد، مقدار آن در سبد باید ۱ واحد افزایش یابد. اگر عنصر تازه در سبد باشد، باید با مقدار ۱ اضافه شود.
تابع add_item(<current_cart>, <items_to_add>) را بسازید که یک دیکشنری سبد خرید و هر تکرارپذیر شبیه فهرست از عنصرهای موردنظر برای افزودن را به عنوان آرگومان میگیرد.
این تابع باید یک دیکشنری سبد خرید جدید/بهروزشده برای کاربر برگرداند.
>>> add_item({'Banana': 3, 'Apple': 2, 'Orange': 1},
('Apple', 'Apple', 'Orange', 'Apple', 'Banana'))
{'Banana': 4, 'Apple': 5, 'Orange': 2}
>>> add_item({'Banana': 3, 'Apple': 2, 'Orange': 1},
['Banana', 'Orange', 'Blueberries', 'Banana'])
{'Banana': 5, 'Apple': 2, 'Orange': 2, 'Blueberries': 1}
اوه. به نظر میرسد تیم محصول دست به افزایش بیرویهی قابلیتها زده است. آنها میخواهند قابلیتهای بیشتری به MVP اضافه کنند. اپلیکیشن حالا باید با خواندن عنصرها از برنامهی یادداشت کاربر، یک سبد خرید بسازد. این برای کاربران راحت است، اما کمی کار بیشتر برای تیم.
تابع read_notes(<notes>) را بسازید که میتواند هر تکرارپذیری شبیه فهرست را به عنوان آرگومان بگیرد.
این تابع باید عنصرها را تجزیه کند و یک سبد خرید/دیکشنری برای کاربر بسازد.
هر عنصر باید با مقدار ۱ اضافه شود.
سپس سبد خرید جدید کاربر باید برگردانده شود.
>>> read_notes(('Banana','Apple', 'Orange'))
{'Banana': 1, 'Apple': 1, 'Orange': 1}
>>> read_notes(['Blueberries', 'Pear', 'Orange', 'Banana', 'Apple'])
{'Blueberries' : 1, 'Pear' : 1, 'Orange' : 1, 'Banana' : 1, 'Apple' : 1}
این اپلیکیشن بخشی با نام «ایدهها» دارد که پر است از دستورهای پخت آماده از سبکهای آشپزی گوناگون. کاربر میتواند هر یک از این دستورهای پخت را انتخاب کند و همهی مواد آن بهصورت خودکار به سبد خریدش اضافه شود. مدیر پروژه از شما خواسته راهی برای ویرایش این دستورهای پخت «ایدهها» بسازید، چون تیم محتوا مدام مواد و مقدارها را تغییر میدهد.
تابع update_recipes(<ideas>, <recipe_updates>) را بسازید که یک دیکشنری «ایدهها» و یک تکرارپذیر از بهروزرسانیهای دستور پخت را به عنوان آرگومان میگیرد.
این تابع باید دیکشنری «ایدهها»ی جدید/بهروزشده را برگرداند.
>>>update_recipes(
{'Banana Bread' : {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1}},
(('Banana Bread', {'Banana': 4, 'Walnuts': 2, 'Flour': 1, 'Butter': 1, 'Milk': 2, 'Eggs': 3}),)
)
...
{'Banana Bread': {'Banana': 4, 'Walnuts': 2, 'Flour': 1, 'Butter': 1, 'Milk': 2, 'Eggs': 3},
'Raspberry Pie': {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1}}
>>> update_recipes(
{'Banana Bread' : {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1},
'Pasta Primavera': {'Eggs': 1, 'Carrots': 1, 'Spinach': 2, 'Tomatoes': 3, 'Parmesan': 2, 'Milk': 1, 'Onion': 1}},
[('Raspberry Pie', {'Raspberry': 3, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1, 'Whipped Cream': 2}),
('Pasta Primavera', {'Eggs': 1, 'Mixed Veggies': 2, 'Parmesan': 2, 'Milk': 1, 'Spinach': 1, 'Bread Crumbs': 1}),
('Blueberry Crumble', {'Blueberries': 2, 'Whipped Creme': 2, 'Granola Topping': 2, 'Yogurt': 3})]
)
...
{'Banana Bread': {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
'Raspberry Pie': {'Raspberry': 3, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1, 'Whipped Cream': 2},
'Pasta Primavera': {'Eggs': 1, 'Mixed Veggies': 2, 'Parmesan': 2, 'Milk': 1, 'Spinach': 1, 'Bread Crumbs': 1},
'Blueberry Crumble': {'Blueberries': 2, 'Whipped Creme': 2, 'Granola Topping': 2, 'Yogurt': 3}}
وقتی کاربری سبدی را آغاز کرده باشد، اپلیکیشن به او اجازه میدهد عنصرهایش را به ترتیب الفبا مرتب کند. این کار پیدا کردن عنصرها را آسانتر میکند و وقتی خطاهای ثبت داده رخ میدهد، مثل داشتن «potatoes» و «Potato» در پایگاه داده، کمک میکند.
تابع sort_entries(<cart>) را بسازید که یک سبد خرید/دیکشنری را به عنوان آرگومان میگیرد و یک سبد جدید و مرتبشده بر اساس الفبا برمیگرداند.
>>> sort_entries({'Banana': 3, 'Apple': 2, 'Orange': 1})
{'Apple': 2, 'Banana':3, 'Orange': 1}
اپلیکیشن باید سبد خرید یک کاربر مشخص را برای آمادهسازی به فروشگاه بفرستد. اما کارکنان فروشگاه باید بدانند هر عنصر در کدام راهروی فروشگاه قرار دارد و آیا به یخچال نیاز دارد یا نه. بنابراین (تا حدی دلبخواه) سبد «آمادهسازی» باید به ترتیب معکوس الفبا مرتب شود و مقدار هر عنصر با اطلاعات مکان و یخچال ترکیب شود.
تابع send_to_store(<cart>, <aisle_mapping>) را بسازید که یک سبد خرید کاربر و یک دیکشنری میگیرد که برای هر عنصر، شمارهی راهروی فروشگاه و یک True/False برای نیاز به یخچال را دارد.
این تابع باید یک سبد «آمادهسازی» ترکیبی را return کند که برای هر عنصری که مشتری سفارش میدهد، (مقدار، راهرو و یخچال) را دارد.
عنصرها باید به ترتیب معکوس الفبا بیایند.
>>> send_to_store({'Banana': 3, 'Apple': 2, 'Orange': 1, 'Milk': 2},
{'Banana': ['Aisle 5', False], 'Apple': ['Aisle 4', False], 'Orange': ['Aisle 4', False], 'Milk': ['Aisle 2', True]})
{'Orange': [1, 'Aisle 4', False], 'Milk': [2, 'Aisle 2', True], 'Banana': [3, 'Aisle 5', False], 'Apple': [2, 'Aisle 4', False]}
اپلیکیشن نمیتواند بیپایان سفارش مشتریها را ثبت کند. دیر یا زود، موجودی محصولهای مختلف فروشگاه تمام میشود. پس MVP شما باید هر بار که کاربری سفارشش را به فروشگاه میفرستد، موجودی فروشگاه را بهروزرسانی کند. وگرنه مشتریها محصولهایی را سفارش میدهند که واقعاً موجود نیستند.
تابع update_store_inventory(<fulfillment_cart>, <store_inventory>) را بسازید که یک سبد «آمادهسازی» و یک موجودی فروشگاه میگیرد.
این تابع باید مقدارهای موجودی فروشگاه را به اندازهی عدد «سفارشدادهشده» در سبد «آمادهسازی» کاهش دهد و سپس موجودی بهروزشدهی فروشگاه را برگرداند.
هر جا تعداد یک محصول در فروشگاه به ۰ برسد، آن عدد باید با پیام «Out of Stock» جایگزین شود.
>>> update_store_inventory({'Orange': [1, 'Aisle 4', False], 'Milk': [2, 'Aisle 2', True], 'Banana': [3, 'Aisle 5', False], 'Apple': [2, 'Aisle 4', False]},
{'Banana': [15, 'Aisle 5', False], 'Apple': [12, 'Aisle 4', False], 'Orange': [1, 'Aisle 4', False], 'Milk': [4, 'Aisle 2', True]})
{'Banana': [12, 'Aisle 5', False], 'Apple': [10, 'Aisle 4', False], 'Orange': ['Out of Stock', 'Aisle 4', False], 'Milk': [2, 'Aisle 2', True]}