دليل شامل لقواميس بايثون: كيفية التكرار والنسخ والدمج في Python 3.9
مقدمة إلى عالم قواميس بايثون: بنية البيانات الأساسية
تُعد بايثون لغة برمجة محورية في مجال علم البيانات، ويُعتبر إتقان التعامل مع المجموعات (collections) مهارة أساسية لا غنى عنها. هذه المجموعات هي هياكل بيانات قادرة على استيعاب عناصر متعددة من أنواع بيانات متنوعة. اليوم، سنغوص في تفاصيل القواميس (dictionaries)، وهي نوع خاص ومميز من المجموعات في بايثون. سنتناول وظائفها الأساسية، آلياتها الداخلية، بالإضافة إلى استعراض بعض الميزات المتطورة في أحدث إصدارات بايثون.
بنهاية هذا الدليل الشامل، ستكون على دراية تامة بما يلي:
- ما هي القواميس (
dictionaries) في بايثون. - كيفية التعامل بفعالية مع القواميس.
- طرق التكرار (
iterate) عبر عناصر القواميس. - الأساليب الصحيحة لنسخ (
copy) القواميس. - كيفية دمج (
merge) القواميس في إصدارPython 3.9.
ما هي القواميس (Dictionaries) في بايثون؟
قبل الخوض في أي موضوع بعمق، من الأفضل دائمًا البدء بتعريف بسيط وواضح. كما ذكرنا سابقًا، القواميس هي نوع من المجموعات في بايثون. ومع ذلك، على عكس القوائم (lists) والمجموعات (sets) والصفوف (tuples)، فإنك لا تقوم بتخزين قيم فردية، بل ما يُعرف بـ أزواج المفتاح-القيمة (key-value pairs). هذا يعني أنك بدلاً من الإشارة إلى قيمك من خلال فهرس (index)، فإنك تستخدم مفتاحًا (key)، وهو معرف فريد لكل قيمة.
l1 = [10, "Hello", True, 20.23] # List
t1 = (10, "Hello", True, 20.23) # Tuple
s1 = {10, "Hello", True, 20.23} # Set
d1 = {'number': 10, 'greeting': "Hello", 'boolean': True, 'float': 20.23} # Dictionary
في المثال أعلاه، يمكنك ملاحظة الفرق الجوهري. يتم فصل أزواج المفتاح-القيمة الفردية بواسطة فواصل (commas). يبدأ كل زوج بمفتاح فريد يتبعه نقطتان رأسيتان (colon) ثم القيمة المقابلة له. لاحظ أن القيمة (value) لا يجب أن تكون فريدة، نظرًا لأننا لا نستخدمها للوصول إلى أي شيء أو تعريفه. تذكر أيضًا أنه يمكننا استخدام أي نوع بيانات نريده للمفاتيح والقيم. هنا، استخدمنا السلاسل النصية (strings) فقط للمعرفات، ولكن يمكننا أيضًا استخدام الأعداد الصحيحة (integers)، والأعداد العشرية (floats)، والمجموعات (collections)، أو حتى القيم المنطقية (Booleans). ومع ذلك، يجب أن تسأل نفسك دائمًا عن مدى منطقية هذا الاختيار؛ ففي معظم الأحيان، ستكون السلسلة النصية هي الخيار الأفضل للمفتاح.
كيفية التعامل مع القواميس في بايثون
الآن بعد أن عرفنا ماهية القواميس، دعنا نلقي نظرة على كيفية التعامل معها. سنبدأ بالعمليات الأساسية مثل الوصول إلى القيم، إضافتها، وحذفها. بعد ذلك، سنتعمق في بعض المواضيع الأكثر تقدمًا وإثارة للاهتمام.
الوصول إلى عناصر القاموس وتعديلها
يمكنك الوصول إلى عناصر القاموس في بايثون بنفس الطريقة التي تصل بها إلى عناصر أي مجموعة أخرى. الفرق الوحيد هو أنك تمرر مفتاحًا (key) بدلاً من فهرس (index). ينطبق هذا أيضًا على تغيير القيم وحتى إلحاق قيم جديدة.
person = {'name': "Mike", 'age': 25, 'weight': 80.5}
print(person['name']) # الوصول إلى القيمة باستخدام المفتاح
person['name'] = "Bob" # تغيير قيمة موجودة
print(person['name'])
person['gender'] = 'm' # إضافة زوج مفتاح-قيمة جديد
print(person['gender'])
كما ترى هنا، ما عليك سوى تمرير مفتاح للوصول إلى القيمة التي يشير إليها. أولاً، نطبع الاسم، ثم نغيره، ثم نطبعه مرة أخرى للتأكد من تطبيق التغييرات. لاحظ أن هذا لا يعمل فقط للأزواج الموجودة بالفعل، بل أيضًا للأزواج الجديدة. لإنشاء زوج مفتاح-قيمة جديد، ما عليك سوى الإشارة إلى مفتاح غير موجود بعد وتعيين قيمة له. يتم بعد ذلك إضافة الزوج إلى القاموس تلقائيًا.
إزالة القيم من القاموس
تختلف عملية إزالة القيم من القاموس. هنا، يمكنك إما استخدام الكلمة المفتاحية del أو الدالة pop(). الفرق الرئيسي بين هذين النهجين هو أن del تزيل زوج المفتاح-القيمة فقط، بينما pop() تُرجع القيمة التي تم إزالتها بعد الحذف. اعتمادًا على حالة الاستخدام الخاصة بك، سيتعين عليك تحديد الطريقة التي تناسب مهمتك بشكل أفضل.
كيفية التكرار (Iterate) عبر القواميس في بايثون
نظرًا لأن القواميس هي مجموعات، يمكنك أيضًا التكرار عبرها. لكن هذه العملية ليست بسيطة ومباشرة كما هو الحال مع أنواع المجموعات الأخرى. هذا لأنك لا تتعامل مع قيم فردية، بل مع أزواج. عندما تقوم بالتكرار عبر قاموس باستخدام حلقة for، فإنك في الواقع تقوم بالتكرار عبر المفاتيح (keys) فقط.
names_ages = {'Bob': 50, 'Anna': 28, 'Max': 30, 'John': 76}
for element in names_ages:
print(element)
# المخرجات:
# Bob
# Anna
# Max
# John
لذلك، إذا كنت ترغب في التكرار عبر القيم (values) أو حتى الأزواج الكاملة، فأنت بحاجة إلى استخدام دوال إضافية. للوصول إلى القيم، ما عليك سوى استدعاء الدالة values(). تُرجع هذه الدالة كائنًا تكراريًا (iterator) لجميع قيم القاموس. للوصول إلى الأزواج الكاملة، يمكنك استدعاء الدالة items(). هنا، ستقوم بالتكرار عبر قائمة من الصفوف (tuples)، حيث يمثل كل صف زوج مفتاح-قيمة واحدًا. بالطبع، توجد أيضًا الدالة keys()، في حال كنت ترغب في التعامل مع مفاتيح القاموس خارج حلقة for.
print(list(names_ages.keys()))
print(list(names_ages.values()))
print(list(names_ages.items()))
# المخرجات:
# > ['Bob', 'Anna', 'Max', 'John']
# > [50, 28, 30, 76]
# > [('Bob', 50), ('Anna', 28), ('Max', 30), ('John', 76)]
من الأمور المهمة التي يجب تذكرها هنا هي أن هذه الدوال لا تُرجع قوائم فعلية. بل تُرجع كائنات (objects) يمكنك استخدامها للتكرار عبر المفاتيح والقيم. ولكن يمكنك بسهولة تحويل هذه الكائنات إلى قوائم باستخدام الدالة list().
كيفية نسخ (Copy) القواميس في بايثون
الآن ننتقل إلى بعض الأمور الأكثر تقدمًا. قد لا تصدق كم مرة يواجه المبرمجون الجدد وغير المتمرسين مشاكل لأنهم ينسخون المجموعات بطريقة خاطئة. يقضون ساعات في استكشاف أخطاء مشاريعهم ولا يتمكنون من العثور على المشكلة. لذا، انتبه جيدًا هنا إذا كنت لا ترغب في تجربة هذا الإحباط بنفسك.
نسخ أنواع البيانات الأولية (Primitive Data Types)
قبل أن نتحدث عن نسخ المجموعات والقواميس، دعنا نلقي نظرة على كيفية نسخ أنواع البيانات الأولية مثل الأعداد الصحيحة (integers).
i1 = 20
i2 = i1
i2 += 10
print(i1, i2)
# المخرجات: 20 30
عندما ترغب في إنشاء متغير جديد ونسخ قيمة عدد صحيح آخر إليه، فإنك تقوم ببساطة بتعيين المتغير مباشرة. بعد ذلك، يمكنك تغيير قيمة العدد الصحيح الثاني والعمل به، دون تغيير أي شيء في العدد الصحيح الأول. ينطبق هذا أيضًا على القيم المنطقية (Booleans)، والأعداد العشرية (Floats)، والسلاسل النصية (Strings)، وما إلى ذلك.
مشكلة النسخ بالمرجع (Reference) في القواميس
ومع ذلك، دعنا نرَ ما يحدث عندما نفعل ذلك مع قاموس:
d1 = {'a': 10, 'b': 20, 'c': 30}
d2 = d1
d2['c'] = 50
print(d1)
print(d2)
# المخرجات:
# {'a': 10, 'b': 20, 'c': 50}
# {'a': 10, 'b': 20, 'c': 50}
ماذا حدث هنا؟ ألم نفعل نفس الشيء كما فعلنا من قبل؟ لماذا يتغير القاموس الأول عندما نعدل القاموس الثاني؟ أليست مجرد نسخة؟ الإجابة واضحة: لا. عندما تقوم بتعيين قاموس لمتغير جديد، فإنك في الواقع تمرر ما يسمى مرجعًا (reference). المتغير الثاني ليس قاموسًا فعليًا، بل هو مجرد متغير آخر يشير إلى نفس القاموس الذي يشير إليه المتغير الأول. لذلك، لا يهم المتغير الذي تطبق عليه التغييرات، نظرًا لأنها تتم جميعها على القاموس الوحيد الذي يشيران إليه.
إنشاء نسخة سطحية (Shallow Copy)
إذا كنت ترغب في إنشاء نسخة سطحية (shallow copy) فعلية لقاموس في بايثون، فأنت بحاجة إما إلى استخدام الدالة dict() أو استدعاء الدالة copy() الخاصة بالقاموس. من خلال القيام بذلك، فإنك تنشئ قاموسًا جديدًا يحتوي على نفس عناصر القاموس الأصلي.
d1 = {'a': 10, 'b': 20, 'c': 30}
d2 = dict(d1)
d3 = d1.copy()
d2['b'] = 50
d3['a'] = -90
print(d1) # لم يتغير
لاحظ، مع ذلك، أن الكائنات (objects) داخل النسخة لا تزال هي نفس الكائنات تمامًا كما في القاموس الأول. لذلك، إذا كانت هذه الكائنات أكثر تعقيدًا أو كانت مجموعات (collections)، فستحصل على قاموس منفصل جديد (لكن الكائنات بداخله ستظل تشير إلى نفس الكائنات الموجودة في القاموس الأول). لتغيير ذلك، سيتعين عليك عمل ما يسمى نسخة عميقة (deep copy)، ولكن هذا خارج نطاق هذه المقالة.
كيفية دمج (Merge) القواميس في Python 3.9
أخيرًا وليس آخرًا، دعنا نتحدث عن ميزات القواميس المتطورة في Python 3.9، والتي تركز على دمج القواميس. حتى وقت قريب، كان على المبرمجين إما استخدام الدالة update() أو الاستفادة من عوامل فك التعبئة (unpacking operators).
الطرق التقليدية لدمج القواميس
d1 = {'a': 10, 'b': 20, 'c': 30}
d2 = {'c': 40, 'd': 60, 'e': 20}
d1.update(d2)
print(d1)
d1 = {'a': 10, 'b': 20, 'c': 30} # إعادة تهيئة d1 للمثال التالي
d3 = {**d1, **d2}
print(d3)
الفرق الرئيسي بين هذين النهجين هو أن الدالة update() تضيف قيم قاموس واحد إلى آخر وتطبق التغييرات مباشرة. لا يتم إرجاع القاموس الناتج، بل يتم حفظه في الكائن الأول. عندما تستخدم عوامل فك التعبئة (unpacking operators)، من ناحية أخرى، فإنك تنشئ قاموسًا جديدًا وتضع أزواج المفتاح-القيمة للقاموسين فيه عن طريق فك تعبئتها.
الآن قد تسأل نفسك ماذا يحدث عندما تدمج قاموسين يحتويان على نفس المفتاح (key) بداخلهما. يمكنك التفكير في الأمر على النحو التالي: ينشئ القاموس الأول زوج المفتاح-القيمة، ويقوم الثاني بالكتابة فوقه. لذلك إذا استدعيت الدالة update على المجموعة الأولى ومررت المجموعة الثانية كوسيطة، فسينتهي زوج المفتاح-القيمة للقاموس الثاني في النتيجة. وينطبق الشيء نفسه على فك التعبئة (unpacking)؛ أي قاموس تمرره أخيرًا يكتب فوق القواميس السابقة.
ميزات الدمج الجديدة في Python 3.9
هذه هي الطريقة القديمة للقيام بالأشياء. في Python 3.9، ومع ذلك، تم تقديم عوامل الدمج (merging operators) والتحديث (updating operators). إنها تجعل عملية ضم القواميس أبسط وأكثر وضوحًا.
d1 = {'a': 10, 'b': 20, 'c': 30}
d2 = {'c': 40, 'd': 60, 'e': 20}
d3 = d1 | d2 # دمج (Merging)
d1 |= d2 # تحديث (Updating)
كما ترى، فإن عامل الدمج (merging operator) هو نفسه الذي يستخدم لعملية OR الثنائية (bitwise OR operation). ترتيب القواميس مهم إذا كان لديك مفاتيح متطابقة في كلا القاموسين. المجموعة الموجودة على اليمين تكتب فوق المجموعة الموجودة على اليسار. إذا كنت ترغب في تحديث القاموس الأول بدلاً من إرجاع قاموس جديد، فما عليك سوى دمج عامل الدمج مع عامل التعيين الأساسي (assignment operator). هذه الطريقة لدمج القواميس هي الطريقة الموصى بها منذ Python 3.9.
الخلاصة التقنية
لقد استعرضنا في هذا الدليل الشامل بنية القواميس في بايثون، وهي أحد أهم هياكل البيانات وأكثرها مرونة. فهم كيفية التعامل مع أزواج المفتاح-القيمة، والتمييز بين النسخ السطحية والعميقة، وإتقان طرق الدمج المختلفة، لا سيما الميزات الجديدة في Python 3.9، يمثل ركيزة أساسية لأي مبرمج بايثون. إن استخدام الأدوات الصحيحة للنسخ والدمج يجنبك الأخطاء الشائعة ويضمن سلامة بياناتك، بينما تتيح لك طرق التكرار المتنوعة معالجة البيانات بكفاءة. هذه المعرفة العميقة لا تعزز فقط من قدراتك البرمجية، بل تمكنك أيضًا من كتابة أكواد أكثر نظافة وفعالية وقابلية للصيانة في مشاريع علم البيانات وتطوير البرمجيات.
تلخيص شامل
الآن، يجب أن تكون مرتاحًا جدًا عند العمل مع القواميس. أنت لا تعرف فقط ماهيتها وكيفية استخدامها، بل تفهم أيضًا كيفية عملها على مستوى أعمق. عند العمل في مشروع، ستعرف كيفية نسخ القواميس بالطريقة الصحيحة. لقد غطينا حتى إحدى الميزات المتطورة لأحدث إصدار من بايثون. تأكد من مراجعة مقتطفات التعليمات البرمجية مرة أخرى وفهم كيفية ولماذا تعمل. هذا سيجعلك مبرمج بايثون أفضل بكثير.