يوفّر صنف dict في Python العديد من الطرق المفيدة للتعامل مع القواميس.
قُدّم بعضها في مفهوم dicts.
نغطي هنا المزيد منها، إلى جانب بعض التقنيات للتنقل عبر القواميس والتعامل معها.
setdefault() للإدراج دون أخطاءكان مفهوم القواميس قد غطّى سابقًا أن .get(key, <default value>) يُرجع قيمة value موجودة أو default value إذا لم يُعثر على key في القاموس، وبذلك يتجنّب KeyError.
يعمل هذا جيدًا في الحالات التي تفضّل فيها عدم وجود معالجة أخطاء إضافية ولكن لا يمكنك الوثوق بأن key المطلوب سيكون موجودًا.
لعملية إدراج "آمنة" مماثلة (دون KeyError)، هناك طريقة .setdefault(key, <default value>).
ستُرجع setdefault(key, <default value>) القيمة value إذا وُجد المفتاح key في القاموس.
إذا لم يُعثر على المفتاح، فستُدرج زوج (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() لتعبئة قاموس من كائن قابل للتكرارلتعبئة قاموس بسرعة بمفاتيح keys متنوعة وقيم افتراضية، فإن طريقة الصنف fromkeys(iterable, <default value>) ستتنقل عبر كائن قابل للتكرار من المفاتيح 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 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 3.7+، تحافظ القواميس dicts على ترتيب إدراج المدخلات، مما يسمح بالتنقل بنمط الوارد أولًا يخرج أولًا (FIFO) عند استخدام .keys() أو .values() أو .items().
في Python 3.8+، أصبحت العروض أيضًا قابلة للعكس.
يتيح هذا التنقل عبر المفاتيح أو القيم أو أزواج (key, value) بترتيب الآخر يدخل أولًا يخرج أولًا (LIFO) باستخدام reversed(<dict>.keys()) أو reversed(<dict>.values()) أو reversed(<dict>.items()):
>>> 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')
لا تمتلك القواميس طريقة ترتيب مدمجة.
ومع ذلك، من الممكن ترتيب عرض القاموس dict باستخدام الدالة المدمجة sorted() مع dict.items().
يمكن بعد ذلك استخدام العرض المُرتَّب لإنشاء قاموس جديد.
وكما في التنقل، يكون الترتيب الافتراضي حسب مفاتيح 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 3.9 وسيلة مختلفة لدمج القواميس 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 أو كائنًا قابلًا للتكرار من أزواج (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.
وظّفتك للتو شركة Mecha Munch™، وهي شركة لأتمتة التسوق من متاجر البقالة، للعمل على تطبيق الطلبات الخاص بها. مهمة فريقك هي بناء MVP (منتج أولي قابل للتطبيق بدرجة دنيا) يدير كل أنشطة سلة التسوق الأساسية، بما يسمح للمستخدمين بإضافة طلبات البقالة وحذفها وترتيبها. لحسن الحظ، يتولى فريق آخر كل وظائف النقود وإتمام الشراء!
ينبغي أن يسمح MVP للمستخدم بإضافة عناصر إلى سلة تسوقه. وقد يكون ذلك عنصرًا واحدًا أو عدة عناصر دفعة واحدة. وبما أن هذا منتج أولي، تُشار إلى كمية العنصر عن طريق التكرار. إذا أراد المستخدم إضافة برتقالتين، فستظهر 'Oranges' مرتين في المُدخلات القابلة للتكرار. إذا كان العنصر موجودًا بالفعل في سلة المستخدم، فينبغي زيادة كميته في السلة بمقدار 1. وإذا كان العنصر جديدًا على السلة، فيُضاف بكمية مقدارها 1.
أنشئ الدالة 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>) التي يمكنها أن تأخذ أي تركيب قابل للتكرار شبيه بالقوائم كوسيط.
ينبغي أن تُحلّل الدالة العناصر وتنشئ سلة تسوق/قاموسًا للمستخدم.
ويُضاف كل عنصر بكمية مقدارها 1.
ثم تُرجع سلة المستخدم الجديدة.
>>> 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>) التي تأخذ "سلة تجهيز" ومخزون متجر.
وينبغي أن تخصم الدالة من كميات مخزون المتجر العدد "المطلوب" في "سلة التجهيز"، ثم تُرجع مخزون المتجر المحدَّث.
وحيثما ينخفض عدد أحد عناصر المتجر إلى 0، يُستبدل العدد بالرسالة '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]}
سجّل في Exercism لتتعلّم وتتقن Python عبر 17 مفهومًا146 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.