تشكل هياكل البيانات اللبنات الأساسية لأي لغة برمجة. ولإنشاء منتجات برمجية قوية وعالية الأداء، يجب على المرء أن يعرف كيفية استخدام هياكل البيانات بكفاءة.
إلى جانب القوائم والمجموعات والمجموعات، تعد القواميس واحدة من أكثر هياكل البيانات المضمنة استخدامًا في بايثون. يمكن تعريف القاموس على أنه مجموعة غير مرتبة من أزواج القيمة والمفتاح. تُستخدم المفاتيح، كما يوحي الاسم، للوصول إلى القيم. يجب أن تكون المفاتيح فريدة وغير قابلة للتغيير، حتى نتمكن من استخدام السلاسل أو الأرقام (int أو float) أو المجموعات كمفاتيح. من ناحية أخرى، يمكن أن تكون القيم من أي نوع. فيما يلي بعض الأمثلة على أزواج القيمة والمفتاح:
KEY: VALUE
‘color’: ‘blue’
‘version’: 3.23
‘operating-system’: ‘ChromeOS’
لا يتم فرز العناصر (أي أزواج القيمة والمفتاح) في القاموس بشكل افتراضي، ولا يوجد لها فهرس. ولكن يمكننا فرز القاموس في بايثون، إما بالفرز حسب المفتاح أو حسب القيمة. في هذه المقالة، سنتعلم كيفية فرز القاموس في بايثون بتفصيل كبير.
كيفية إنشاء قاموس
توجد طرق مختلفة لإنشاء قاموس في بايثون. إحدى الطرق هي كتابة صفر أو أكثر من أزواج القيمة الرئيسية بين الأقواس المتعرجة. فيما يلي بعض الأمثلة:
>>> # creating an empty dictionary
>>> empty_dict = {}
>>> print(type(empty_dict))
<class 'dict'="">
>>> # creating grades dictionary
>>> grades = {'Dennis':'A', 'Betty':'B', 'Antoine':'C', 'Ally':'A'}
>>> print(grades)
{'Dennis': 'A', 'Betty': 'B', 'Antoine': 'C', 'Ally': 'A'}
</class>
يتكون قاموس الدرجات من أربعة أزواج مفتاح-قيمة، حيث تمثل الأسماء المفاتيح وتمثل الدرجات الحرفية القيم.
يمكننا أيضًا استخدام منشئ dict()
لإنشاء قاموس. يمكن تمرير أزواج القيمة والمفتاح كقائمة من الثنائيات، حيث يمثل كل ثنائي زوج قيمة ومفتاح:
>>> # creating grades dictionary with dict constructor
>>> grades = dict([("Dennis","A"), ("Betty","B"), ("Antoine","C"), ("Ally","A")])
>>> print(grades)
{'Dennis': 'A', 'Betty': 'B', 'Antoine': 'C', 'Ally': 'A'}
إذا كانت المفاتيح عبارة عن سلاسل بسيطة، فيمكننا تحديد أزواج القيمة والمفتاح باستخدام وسيطات الكلمات الأساسية في منشئ dict()
، وهو أبسط من استخدام قائمة من العناصر:
>>> # creating grades dictionary using keyword arguments
>>> grades = dict(Dennis="A", Betty="B", Antoine="C", Ally="A")
>>> print(grades)
{'Dennis': 'A', 'Betty': 'B', 'Antoine': 'C', 'Ally': 'A'}
نقترب الآن من الموضوع الرئيسي لهذه المقالة، وهو كيفية فرز القاموس. دعونا أولاً نتناول دالة sorted()
المضمنة في بايثون.
دالة ()sorted
سنستعرض بعض الأمثلة لمساعدتك في الحصول على فهم شامل لكيفية فرز القاموس في بايثون. ولكن أولاً، دعنا نفهم الدالة sorted()
، التي تأخذ عنصرًا قابلاً للتكرار كحجة وتعيد نسخة مرتبة من هذا العنصر القابل للتكرار.
فيما يلي مثال لفرز قائمة من السلاسل:
>>> names = ["Max", "Jonathan", "David", "Ashely"]
>>> print(sorted(names)
['Ashely', 'David', 'Jonathan', 'Max']
من المهم أن نذكر أن الدالة sorted()
لا تعمل في مكانها، مما يعني أنها لا تعدل المتغير الأصلي (أي في المثال أعلاه، قائمة الأسماء). بدلاً من ذلك، تقوم بإرجاع نسخة معدلة من المتغير الأصلي. لاستخدام قائمة الأسماء المفرزة، نحتاج إلى تعيينها لمتغير جديد أو للمتغير نفسه.
>>> names = ["Max", "Jonathan", "David", "Ashely"]
>>> names = sorted(names)
>>> print(names)
['Ashely', 'David', 'Jonathan', 'Max']
تأخذ الدالة sorted()
أيضًا وسيطتين اختياريتين، وهما key (سنشرحهما أدناه) وreverse
. الوسيطة reverse
واضحة بذاتها؛ فعند ضبطها على “True
“، تتم عملية الفرز بترتيب عكسي.
>>> names = ["Max", "Jonathan", "David", "Ashely"]
>>> names = sorted(names, reverse=True)
>>> print(names)
['Max', 'Jonathan', 'David', 'Ashely']
عند فرز قائمة من السلاسل، فإن الفرز حسب الترتيب الأبجدي هو الشيء الطبيعي الذي يجب القيام به. وعلى نحو مماثل، عند فرز عنصر قابل للتكرار رقميًا، يتم ذلك بترتيب تصاعدي أو تنازلي حسب قيمة المعلمة العكسية.
يمكن أيضًا استخدام الدالة sorted() لإجراء عمليات فرز أكثر تعقيدًا باستخدام معلمة المفتاح. لنفترض أن لدينا قائمة من العناصر ونريد فرزها:
>>> employees = [("Max", 27), ("Jonathan", 35), ("David", 32),
>>> ("Ashley", 28)]
>>> print(sorted(employees)
[('Ashley', 28), ('David', 32), ('Jonathan', 35), ('Max', 27)]
يتم فرز الصفوف بشكل افتراضي حسب العناصر الأولى (أي المفاتيح)، وهي أسماء الموظفين في هذه الحالة. ماذا لو أردنا فرز أزواج المفتاح والقيمة بناءً على القيم، والتي هي العناصر الثانية في هذه المجموعات؟ في هذه الحالة، يمكننا استخدام معلمة المفتاح:
>>> employees = [("Max", 27), ("Jonathan", 35), ("David", 32),
>>> ("Ashley", 28)]
>>> print(sorted(employees, key=lambda employee: employee[1])
[('Max', 27), ('Ashley', 28), ('David', 32), ('Jonathan', 35)]
تستخدم دالة lambda
كحجة رئيسية تأخذ العنصر الثاني (أي العنصر الذي يحمل الفهرس 1) من كل مجموعة. يمكننا أيضًا استخدام حجتي المفتاح والعكس معًا.
>>> employees = [("Max", 27), ("Jonathan", 35), ("David", 32),
>>> ("Ashley", 28)]
>>> print(sorted(employees, key=lambda employee: employee[1], reverse=True)
[('Jonathan', 35), ('David', 32), ('Ashley', 28), ('Max', 27)]
كيفية فرز القاموس في بايثون
مفاتيح القاموس والقيم والعناصر
قبل أن نصل إلى فرز القاموس في بايثون، دعونا أولاً نتعلم كيفية استخراج أجزاء من المعلومات من القاموس.
كما ذكرنا سابقًا، يتكون القاموس من أزواج مفتاح-قيمة. يمكننا استخراج المفاتيح والقيم بشكل منفصل من القاموس باستخدام التابعين keys()
وvalues()
على التوالي.
>>> employees = {"Max": 27, "Jonathan": 35, "David": 32, "Ashley": 28}
>>> print(employees.keys())
dict_keys(['Max', 'Jonathan', 'David', 'Ashley'])
>>> print(employees.values())
dict_values([27, 35, 32, 28])
يمكننا أيضًا استخراج أزواج القيمة الرئيسية كقائمة من العناصر باستخدام طريقة items()
:
>>> print(employees.items())
dict_items([('Max', 27), ('Jonathan', 35), ('David', 32), ('Ashley', 28)])
فرز القاموس حسب المفتاح والقيمة
لفرز القاموس حسب المفاتيح أو القيم، نأخذ العناصر أولاً (أي أزواج المفتاح والقيمة) ثم نستخدم معلمة المفتاح في الدالة sorted()
لاختيار المفاتيح أو القيم مع الفهرسة. نظرًا لأن العنصر يتم استخراجه كمجموعة من المفتاح والقيمة، فإن العنصر الأول (مع الفهرس 0) هو المفتاح والعنصر الثاني (مع الفهرس 1) هو القيمة.
>>> sort by keys
>>> sorted(employees.items(), key=lambda item: item[0])
[('Ashley', 28), ('David', 32), ('Jonathan', 35), ('Max', 27)]
كما نرى في الناتج أعلاه، فإن البنية النهائية عبارة عن قائمة من الثنائيات، وليس قاموسًا. ويرجع هذا إلى سلوك الدالة sorted()
، التي ترجع قائمة من الثنائيات. إذا أردنا الحصول على الناتج النهائي كقاموس، فما علينا سوى تطبيق منشئ dict()
على ناتج الدالة sorted()
.
>>> sort by keys
>>> dict(sorted(employees.items(), key=lambda item: item[0]))
{'Ashley': 28, 'David': 32, 'Jonathan': 35, 'Max': 27}
الآن، يكون الناتج عبارة عن قاموس. تذكر أن القاموس الأصلي (أي الموظفين) لم يتم تعديله. بدلاً من ذلك، قمنا بإنشاء قاموس جديد يحتوي على عناصر مرتبة حسب المفاتيح. يمكننا حفظه في متغير جديد.
دعونا الآن نقوم بفرز قاموس الموظفين حسب القيم.
>>> sort by values
>>> employees_sorted = dict(sorted(employees.items(),
>>> key=lambda item: item[1]))
>>> print(employees_sorted)
{'Max': 27, 'Ashley': 28, 'David': 32, 'Jonathan': 35}
فرز القاموس حسب معايير أكثر تعقيدًا
باستخدام دالة lambda كوسيطة للمعلمة الرئيسية وإمكانية الوصول إلى كل من المفاتيح والقيم، يمكننا أيضًا تحديد معايير أكثر تعقيدًا للفرز. دعنا أولاً ننشئ قاموسًا جديدًا:
>>> employees = {
>>> "Max": {"Department": "Engineering", "Age": 27, "Starting Date": 2020},
>>> "David": {"Department": "Marketing", "Age": 32, "Starting Date": 2018},
>>> "Jonathan": {"Department": "HR", "Age": 35, "Starting Date": 2021},
>>> "Ashley": {"Department": "Marketing", "Age": 28, "Starting Date": 2019}
>>> }
في هذا القاموس الجديد، كل قيمة هي أيضًا قاموس – لذا لدينا قاموس داخل قاموس، يُعرف أيضًا باسم القاموس المتداخل. يمكننا فرز قاموس الموظفين حسب إحدى القيم الموجودة في القاموس المتداخل.
يوضح المثال التالي كيفية استخدام قيم تاريخ البدء المتداخلة لفرز قاموس employees:
>>> dict(sorted(employees.items(),
>>> key=lambda item: item[1]["Starting Date"]))
{'David': {'Department': 'Marketing', 'Age': 32, 'Starting Date': 2018},
'Ashley': {'Department': 'Marketing', 'Age': 28, 'Starting Date': 2019},
'Max': {'Department': 'Engineering', 'Age': 27, 'Starting Date': 2020},
'Jonathan': {'Department': 'HR', 'Age': 35, 'Starting Date': 2021}}
يمكننا أيضًا الفرز حسب قيم متعددة. في المثال التالي، تنشئ دالة lambda مجموعة من قيم القسم والعمر، والتي تُستخدم بعد ذلك لفرز العناصر في قاموس الموظفين. تقوم أولاً بالفرز حسب القسم؛ ويتم فرز الموظفين في نفس القسم حسب أعمارهم.
>> dict(sorted(employees.items(),
>>> key=lambda item: (item[1]["Department"], item[1]["Age"])))
{'Max': {'Department': 'Engineering', 'Age': 27, 'Starting Date': 2020},
'Jonathan': {'Department': 'HR', 'Age': 35, 'Starting Date': 2021},
'Ashley': {'Department': 'Marketing', 'Age': 28, 'Starting Date': 2019},
'David': {'Department': 'Marketing', 'Age': 32, 'Starting Date': 2018}}
مشكلات أداء فرز القاموس
نحن بحاجة دائمًا إلى مراعاة القضايا المتعلقة بالأداء، حيث تعد قابلية التوسع مفهومًا أساسيًا في كتابة برامج قوية وفعالة. الوقت وتعقيد التشغيل هما العاملان الرئيسيان اللذان يؤثران على أداء الفرز.
قد تستغرق عملية الفرز قدرًا كبيرًا من الوقت إذا كانت البيانات كبيرة. وبالتالي، فمن الأفضل اختيار طرق أسرع وأكثر أداءً إذا كانت هناك أي طرق. في أمثلتنا، استخدمنا وظائف lambda لاستخراج مفاتيح أو قيم العناصر في القاموس. يمكننا أيضًا استخدام دالة itemgetter
في وحدة المشغل المضمنة. يمكن استخدامها للحصول على العنصر الأول (الفهرس 0) أو العنصر الثاني (الفهرس 1) من عناصر القاموس.
دعنا نقارن بين الطريقتين في قاموس مكون من 10 عناصر. الخطوة الأولى هي إنشاء قاموس:
>>> employees = {
>>> "1001": 27,
>>> "1002": 32,
>>> "1003": 35,
>>> "1004": 28,
>>> "1005": 21,
>>> "1006": 34,
>>> "1007": 29,
>>> "1008": 29,
>>> "1009": 24,
>>> "1010": 28
>>> }
يمكننا قياس وقت التنفيذ باستخدام وحدة timeit
:
>>> from timeit import timeit
>>> stmt = 'dict(sorted(employees.items(), key=lambda item: item[1]))'
>>> timeit(stmt, globals=globals())
0.97 seconds
لنستخدم دالة itemgetter
كوسيطة للمعلمة الرئيسية:
>>> from operator import itemgetter
>>> from timeit import timeit
>>> stmt = 'dict(sorted(employees.items(), key=itemgetter(1))'
>>> timeit(stmt, globals=globals())
0.79 seconds
باستخدام دالة itemgetter
، استغرق الفرز وقتًا أقل بنسبة 19% تقريبًا. قد لا يبدو هذا تحسنًا مهمًا عند العمل على قاموس مكون من 10 عناصر، ولكنه سيكون مهمًا عندما تصبح مجموعة البيانات كبيرة.
اكتشاف المزيد من بايثون العربي
اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.