cb
codebamin
الفصل 12 · القواميس (Dictionaries)

إنشاء وتحديث أزواج المفاتيح والقيم

سنتعلم الآن كيف نضيف أزواج مفتاح وقيمة جديدة إلى القاموس، مثلما نحتاج عند تسجيل ما يشتريه العميل.

قبل القواميس، لنراجع سريعًا طريقة إضافة عناصر إلى قائمة. شغّل البرنامج:

__copyable__ __program_indented__

بسيط. ويمكننا أيضًا تغيير القيمة الموجودة عند فهرس معين واستبدالها بقيمة أخرى:

__copyable__ __program_indented__

ماذا لو حاولنا استخدام الإسناد بالفهرس لإنشاء عناصر القائمة من البداية؟ نعرف أننا نريد cart[0] مساوية لـ'dog' وcart[1] مساوية لـ'box'، فلنجرب أن نقول ذلك مباشرة:

__copyable__ __program_indented__

هذا غير مسموح مع القوائم: الإسناد باستخدام الفهرس يعمل فقط مع فهرس صالح موجود بالفعل.

لكن القواميس مختلفة. جرّب:

__program_indented__

لاحظ أن {} تعني قاموسًا فارغًا، أي لا يحتوي على أي أزواج مفتاح وقيمة. وهي تشبه [] للقائمة الفارغة و"" للنص الفارغ.

عندما تسند قيمة إلى مفتاح غير موجود في قاموس، يُنشأ زوج المفتاح والقيمة الجديد.

هذا بالضبط ما نحتاجه. سواء أراد العميل 500 كلب أو خمسة ملايين، نستطيع وضع المعلومة مباشرة في قاموس الكميات.

اكتب دالة عامة اسمها buy_quantity(quantities, item, quantity) تضيف زوجًا جديدًا من المفتاح والقيمة إلى قاموس quantities:

__copyable__ def buy_quantity(quantities, item, quantity): ...

def test(): quantities = {} buy_quantity(quantities, 'dog', 500) check_result(quantities, {'dog': 500}) buy_quantity(quantities, 'box', 2) check_result(quantities, {'dog': 500, 'box': 2})

test()

يجب أن تعدّل buy_quantity القاموس الذي استلمته مباشرة، ولا تحتاج إلى return أو print. يمكنك افتراض أن item غير موجود مسبقًا في quantities.

جسم الدالة يحتاج سطرًا واحدًا فقط. حوّل الفكرة المحددة quantities['dog'] = 500 إلى صيغة عامة تستخدم المتغيرين item وquantity من دون وضعهما بين علامات اقتباس.

أحسنت! جرّب الدالة الآن بصورة تفاعلية:

__copyable__ __program_indented__

انتبه إلى الجزء int(input()): الدالة input() تعيد نصًا، بينما quantity يجب أن تكون عددًا صحيحًا. إذا أدخل المستخدم شيئًا ليس رقمًا فسيحدث خطأ، وهذا مقبول في هذه المرحلة.

شكرًا للتسوق معنا! لنحسب الآن المبلغ الذي أنفقته على كل نوع من المنتجات على حدة.

كتبنا سابقًا total_cost(quantities, prices) التي تعيد رقمًا واحدًا يمثل الإجمالي الكلي. الآن اكتب total_cost_per_item(quantities, prices) بحيث تعيد قاموسًا جديدًا فيه التكلفة الإجمالية لكل عنصر:

__copyable__ def total_cost_per_item(quantities, prices): totals = {} for item in quantities: ___ = quantities[item] * prices[item] return totals

check_result( total_cost_per_item({'apple': 2}, {'apple': 3, 'box': 5}), {'apple': 6}, )

check_result( total_cost_per_item({'dog': 500, 'box': 2}, {'dog': 100, 'box': 5}), {'dog': 50000, 'box': 10}, )

املأ الجزء ___. في الاختبار الأول اشترى العميل تفاحتين وسعر الواحدة 3، لذلك يجب أن يحتوي القاموس الجديد على 'apple': 6. المفتاح هو item، والقيمة هي quantities[item] * prices[item]، والقاموس الذي نبنيه هو totals.

ممتاز! لنرجع إلى مثال الترجمة.

لنفترض أن لدينا قاموسًا من الإنجليزية إلى الفرنسية، وقاموسًا آخر من الفرنسية إلى الألمانية. استخدمهما لإنشاء قاموس جديد من الإنجليزية إلى الألمانية:

__copyable__ def make_english_to_german(english_to_french, french_to_german): ...

check_result( make_english_to_german( {'apple': 'pomme', 'box': 'boite'}, {'pomme': 'apfel', 'boite': 'kasten'}, ), {'apple': 'apfel', 'box': 'kasten'}, )

أنشئ قاموسًا فارغًا، ومرّ على الكلمات الإنجليزية في القاموس الأول. قيمة الكلمة الإنجليزية في القاموس الأول هي الكلمة الفرنسية، وهذه الكلمة الفرنسية هي مفتاح في القاموس الثاني، ومنه تحصل على الكلمة الألمانية. أضف النتيجة إلى القاموس الجديد ثم أعده في النهاية.

عمل رائع!

اكتب الآن دالة تأخذ قاموسًا وتعيد قاموسًا جديدًا بعد تبديل المفاتيح والقيم؛ أي يتحول الزوج a: b إلى b: a:

__copyable__ def swap_keys_values(d): ...

check_result( swap_keys_values({'apple': 'pomme', 'box': 'boite'}), {'pomme': 'apple', 'boite': 'box'}, )

لا تعدّل القاموس الأصلي d. أنشئ قاموسًا فارغًا جديدًا، ومرّ على مفاتيح d. لكل مفتاح، احصل على قيمته، ثم استخدم هذه القيمة كمفتاح جديد واجعل المفتاح القديم هو القيمة الجديدة.

ممتاز!

لكن من المهم أن تعرف أين يمكن أن تفشل فكرة تبديل المفاتيح والقيم. كما يمكن أن يكون لمنتجين في المتجر السعر نفسه، يمكن لكلمتين إنجليزيتين أن تكون لهما الترجمة الفرنسية نفسها.

إذا احتوى القاموس الأصلي على قيم مكررة، فماذا يحدث عندما تجعل هذه القيم مفاتيح؟ مفاتيح القاموس يجب أن تكون فريدة، لذلك ستضيع بعض البيانات.

مثلًا، الكلمة الفرنسية 'avocat' قد تعني avocado أو lawyer. من السهل ترجمة كل كلمة إنجليزية إلى الفرنسية، لكن عند رؤية 'avocat' وحدها لا نعرف أي معنى إنجليزي كان المقصود.

حاول توقع ما سيطبعه الكود التالي قبل تشغيله:

__copyable__ __program_indented__

تعتمد النتيجة على ترتيب المفاتيح في القاموس الأصلي. إذا لم يكن السبب واضحًا، فتتبّع تنفيذ الكود خطوة بخطوة وراقب متى تُكتب كل قيمة داخل القاموس الجديد.

ومع ذلك توجد حالات كثيرة تستطيع فيها التأكد من أن قيم القاموس الأصلية فريدة، وعندها يكون «عكس» القاموس منطقيًا. رأينا سابقًا هذا المثال:

__copyable__ __no_auto_translate__ def substitute(string, d): result = "" for letter in string: result += d[letter] return result

plaintext = 'helloworld' encrypted = 'qpeefifmez' letters = {'h': 'q', 'e': 'p', 'l': 'e', 'o': 'f', 'w': 'i', 'r': 'm', 'd': 'z'} reverse = {'q': 'h', 'p': 'e', 'e': 'l', 'f': 'o', 'i': 'w', 'm': 'r', 'z': 'd'} check_result(substitute(plaintext, letters), encrypted) check_result(substitute(encrypted, reverse), plaintext)

والآن نستطيع إنشاء reverse تلقائيًا:

reverse = swap_keys_values(letters)

لكي ينجح هذا يجب أن تكون كل القيم في letters فريدة. إذا استُبدل كل من 'h' و'j' بالقيمة 'q' أثناء التشفير، فلن توجد طريقة عند فك التشفير لمعرفة هل 'qpeef' كانت أصلًا 'hello' أم 'jello'.

تهانينا! وصلت إلى نهاية الجزء المتاح حاليًا من الدورة. سيُضاف المزيد لاحقًا.

solution.py
1
2
3
4
5
6
7
8
9
10
الطرفية (shell)
>>>