الفصل السابع: هياكل البيانات – القوائم، المجموعات، والقواميس
مقدمة الفصل
حتى الآن، تعاملنا مع أنواع بيانات أساسية تخزن قيمة واحدة فقط (مثل رقم أو نص). لكن في البرمجة الحقيقية، نادرًا ما تكون البيانات بهذه البساطة. غالبًا ما نحتاج إلى التعامل مع مجموعات من البيانات المترابطة، مثل قائمة بأسماء الطلاب، أو بطاقة معلومات لموظف تحتوي على اسمه وعمره وراتبه.
هنا تأتي أهمية هياكل البيانات (Data Structures). هي طرق متخصصة لتنظيم وتخزين البيانات لتسهيل الوصول إليها وإدارتها بكفاءة. في هذا الفصل، سنتعمق في أهم ثلاثة هياكل بيانات أساسية في بايثون، والتي ستستخدمها في كل مشاريعك تقريبًا:
- القوائم (Lists): مجموعات مرتبة وقابلة للتعديل.
- المجموعات الثابتة (Tuples): مجموعات مرتبة ولكن غير قابلة للتعديل.
- القواميس (Dictionaries): مجموعات لتخزين البيانات على شكل “مفتاح-قيمة”.
1. القوائم (Lists)
القائمة هي أكثر هياكل البيانات مرونة وشيوعًا في بايثون. يمكنك التفكير فيها كحاوية ديناميكية يمكنك إضافة عناصر إليها أو حذفها أو تعديلها في أي وقت.
خصائص القائمة:
- مرتبة (Ordered): العناصر تحافظ على ترتيبها الذي أُضيفت به.
- قابلة للتعديل (Mutable): يمكنك تغيير محتوياتها بعد إنشائها.
- تسمح بالتكرار: يمكن أن تحتوي على نفس العنصر أكثر من مرة.
إنشاء قائمة:
لإنشاء قائمة، نضع العناصر بين قوسين مربعين []
ونفصل بينها بفاصلة.
# A list of strings
fruits = ["apple", "banana", "cherry"]
# A list of integers
numbers = [10, 20, 30, 40]
# A list with mixed data types
mixed_list = ["Ahmed", 25, True, 99.5]
print(fruits)
print(numbers)
print(mixed_list)
شرح الكود:
في هذا المثال، قمنا بإنشاء ثلاث قوائم مختلفة. fruits
تحتوي على نصوص، و numbers
تحتوي على أرقام، بينما mixed_list
توضح مرونة القوائم في احتواء أنواع بيانات مختلفة في نفس الوقت.
الوصول إلى عناصر القائمة (Indexing)
يمكنك الوصول إلى أي عنصر في القائمة باستخدام فهرسه (index). تذكر دائمًا أن الفهرسة في بايثون تبدأ من الصفر 0
.
# Define a list of programming languages
languages = ["Python", "JavaScript", "Java", "C++"]
# Access the first element (index 0)
first_language = languages[0] # الوصول للعنصر الأول
print(f"First element: {first_language}")
# Access the third element (index 2)
third_language = languages[2] # الوصول للعنصر الثالث
print(f"Third element: {third_language}")
# Access the last element using negative indexing
last_language = languages[-1] # الوصول للعنصر الأخير
print(f"Last element: {last_language}")
شرح الكود:
نستخدم الأقواس المربعة []
بعد اسم القائمة ونضع بداخلها رقم الفهرس. languages[0]
يعطينا العنصر الأول وهو “Python”. بايثون تدعم أيضًا الفهرسة السلبية، حيث [-1]
يشير دائمًا إلى العنصر الأخير, و [-2]
إلى العنصر قبل الأخير، وهكذا.
تعديل عناصر القائمة
بما أن القوائم قابلة للتعديل (mutable)، يمكنك تغيير قيمة أي عنصر بسهولة.
# Define a list of numbers
numbers = [1, 2, 3, 4, 5]
print(f"Original list: {numbers}")
# Change the value of the second element (index 1)
numbers[1] = 200 # تغيير قيمة العنصر الثاني
print(f"Modified list: {numbers}")
شرح الكود:
قمنا بالوصول إلى العنصر في الفهرس 1
(وهو الرقم 2
) وقمنا بإسناد قيمة جديدة له وهي 200
. عند طباعة القائمة مرة أخرى، نرى أنها تغيرت.
أهم دوال القوائم
توفر بايثون دوال مدمجة مفيدة جدًا للتعامل مع القوائم:
# Start with a list
my_list = ["a", "b", "c"]
print(f"Initial list: {my_list}")
# 1. Add an element to the end
my_list.append("d") # إضافة عنصر للنهاية
print(f"After append: {my_list}")
# 2. Insert an element at a specific index
my_list.insert(1, "x") # إضافة عنصر في مكان محدد
print(f"After insert: {my_list}")
# 3. Remove the first occurrence of an element
my_list.remove("b") # حذف عنصر معين
print(f"After remove: {my_list}")
# 4. Remove and return the last element
last_item = my_list.pop() # حذف وإرجاع آخر عنصر
print(f"Popped item: {last_item}")
print(f"List after pop: {my_list}")
شرح الكود:
append
: هي الطريقة الأكثر شيوعًا لإضافة عنصر جديد، حيث يتم وضعه دائمًا في نهاية القائمة.insert
: تعطيك تحكمًا أكبر، حيث تحدد الفهرس الذي تريد وضع العنصر الجديد فيه.remove
: تبحث عن أول تطابق للقيمة التي تحددها وتقوم بحذفها.pop
: مفيدة عندما تريد حذف العنصر الأخير وفي نفس الوقت الحصول على قيمته لاستخدامها في مكان آخر.
2. المجموعات الثابتة (Tuples)
المجموعة الثابتة، أو الـ tuple
، تشبه القائمة إلى حد كبير، لكن مع فرق جوهري واحد: هي غير قابلة للتعديل (Immutable). بمجرد إنشائها، لا يمكنك تغيير محتوياتها.
خصائص المجموعة الثابتة:
- مرتبة (Ordered): تحافظ على ترتيب عناصرها.
- غير قابلة للتعديل (Immutable): لا يمكن تغييرها بعد الإنشاء.
- تسمح بالتكرار.
إنشاء مجموعة ثابتة:
لإنشاء tuple
، نستخدم الأقواس العادية ()
.
# A tuple of coordinates
coordinates = (10.0, 20.5)
# A tuple of RGB colors
primary_colors = ("Red", "Green", "Blue")
print(coordinates)
print(primary_colors[0]) # الوصول للعناصر بنفس طريقة القوائم
# The following line would cause a TypeError because tuples are immutable
# primary_colors[0] = "Yellow"
شرح الكود:
التعامل مع الـ tuple
من حيث الوصول للعناصر مشابه تمامًا للقوائم. لكن السطر الأخير في التعليقات يوضح نقطة القوة (والضعف) في الـ tuple
؛ أي محاولة لتغيير عنصر ستؤدي إلى خطأ برمجي.
متى نستخدم الـ tuple
؟
- عندما يكون لديك بيانات لا ينبغي أن تتغير أبدًا، مثل إحداثيات جغرافية، أو ألوان ثابتة، أو إعدادات برنامج.
- استخدامها يوفر حماية ضد التغيير العرضي للبيانات.
- قد تكون أسرع بشكل طفيف في الأداء من القوائم في بعض الحالات.
3. القواميس (Dictionaries)
القاموس هو هيكل بيانات فريد لا يعتمد على الفهرسة الرقمية، بل يخزن البيانات على شكل أزواج من مفتاح وقيمة (key-value pairs). يمكنك التفكير فيه كقاموس لغوي حقيقي، حيث تبحث عن كلمة (المفتاح) لتحصل على تعريفها (القيمة).
خصائص القاموس:
- غير مرتب (Unordered) (في نسخ بايثون قبل 3.7) / مرتب (في 3.7 وما بعدها): كقاعدة عامة، لا تعتمد على ترتيب عناصر القاموس.
- قابل للتعديل (Mutable): يمكنك إضافة وتعديل وحذف أزواج المفتاح-القيمة.
- المفاتيح فريدة (Unique Keys): لا يمكن أن يحتوي القاموس على مفتاحين متماثلين.
إنشاء قاموس:
لإنشاء قاموس، نستخدم الأقواس المعقوفة {}
ونحدد أزواج المفتاح-القيمة.
# A dictionary representing a student
student = {
"name": "Fatima", # المفتاح هو "name"، والقيمة هي "Fatima"
"age": 22, # المفتاح هو "age"، والقيمة هي 22
"major": "Computer Science", # المفتاح هو "major"، والقيمة هي "Computer Science"
"is_active": True
}
شرح الكود: كل عنصر في القاموس يتكون من جزأين: مفتاح (عادة ما يكون نصًا) وقيمة (يمكن أن تكون أي نوع بيانات). نستخدم المفتاح للوصول إلى القيمة المرتبطة به.
الوصول إلى قيم القاموس وتعديلها
# Accessing a value using its key
student_name = student["name"] # الوصول للقيمة عبر المفتاح
print(f"Student Name: {student_name}")
# Modifying a value
student["age"] = 23 # تعديل قيمة موجودة
print(f"Updated Age: {student['age']}")
# Adding a new key-value pair
student["gpa"] = 3.8 # إضافة زوج جديد
print(f"Student record after adding GPA: {student}")
شرح الكود:
نستخدم نفس صيغة الأقواس المربعة []
ولكن بدلاً من رقم الفهرس، نضع اسم المفتاح. إذا كان المفتاح موجودًا، يمكننا تعديل قيمته. وإذا لم يكن موجودًا، فسيقوم بايثون بإضافة هذا الزوج الجديد من المفتاح-القيمة إلى القاموس.
الوصول الآمن باستخدام دالة get
إذا حاولت الوصول إلى مفتاح غير موجود باستخدام الأقواس المربعة []
، سيحدث خطأ KeyError
ويتوقف برنامجك. الطريقة الأكثر أمانًا هي استخدام دالة get
.
# Using get() to safely access a key
student_major = student.get("major")
print(f"Major: {student_major}")
# Accessing a non-existent key with get() returns None instead of an error
student_address = student.get("address") # "address" غير موجود
print(f"Address: {student_address}") # سيطبع None
# We can also provide a default value
student_status = student.get("status", "Active") # توفير قيمة افتراضية
print(f"Status: {student_status}")
شرح الكود:
دالة get
تبحث عن المفتاح، إذا وجدته، تعيد قيمته. إذا لم تجده، تعيد القيمة None
(التي تمثل “لا شيء” في بايثون) ولا تسبب أي خطأ. يمكنك أيضًا تزويدها بقيمة افتراضية اختيارية ليتم إرجاعها في حال عدم وجود المفتاح.
4. تمرين تطبيقي: إدارة مخزون المنتجات
المطلوب:
كتابة برنامج بسيط لإدارة مخزون متجر صغير. البرنامج يجب أن يستخدم قائمة لتخزين المنتجات، وكل منتج يجب أن يكون قاموسًا يحتوي على معلوماته (الاسم، السعر، والكمية).
إرشادات الحل:
- إنشاء هيكل البيانات الرئيسي: ابدأ بإنشاء قائمة فارغة باسم
inventory
. هذه القائمة ستحتوي على كل منتجاتك. - تعريف المنتجات: أنشئ ثلاثة قواميس منفصلة، كل قاموس يمثل منتجًا. يجب أن يحتوي كل قاموس على ثلاثة مفاتيح:
"name"
(اسم المنتج)"price"
(سعره)"quantity"
(الكمية المتوفرة)
- إضافة المنتجات إلى المخزون: استخدم دالة
append
لإضافة القواميس الثلاثة التي أنشأتها إلى قائمةinventory
. - عرض المخزون: قم بكتابة حلقة
for
للتكرار على قائمةinventory
. داخل الحلقة، لكل منتج (قاموس)، قم بطباعة معلوماته بشكل منظم وواضح. - حساب القيمة الإجمالية: بعد الحلقة، قم بحساب وعرض القيمة الإجمالية لكل المنتجات في المخزون (أي مجموع
price * quantity
لكل منتج).
الحل المقترح:
# 1. Create the main data structure
inventory = [] # إنشاء قائمة فارغة لتخزين المنتجات
# 2. Define products as dictionaries
product1 = {"name": "Laptop", "price": 4500, "quantity": 10}
product2 = {"name": "Mouse", "price": 120, "quantity": 50}
product3 = {"name": "Keyboard", "price": 250, "quantity": 30}
# 3. Add products to the inventory list
inventory.append(product1) # إضافة المنتج الأول
inventory.append(product2) # إضافة المنتج الثاني
inventory.append(product3) # إضافة المنتج الثالث
# 4. Display the inventory
print("--- Current Store Inventory ---")
total_value = 0
for item in inventory:
# Calculate the value of the current item stock
item_stock_value = item["price"] * item["quantity"]
total_value += item_stock_value # إضافة قيمة المخزون للقيمة الإجمالية
# Print item details
print(
f"Item: {item['name']}, "
f"Price: {item['price']} DH, "
f"Quantity: {item['quantity']}, "
f"Stock Value: {item_stock_value} DH"
)
# 5. Display the total inventory value
print("-------------------------------")
print(f"Total Value of all products in inventory: {total_value} DH")
5. خلاصة الفصل
- القوائم (Lists) هي الخيار الأمثل عندما تحتاج إلى مجموعة مرتبة من العناصر يمكنك تعديلها باستمرار.
- المجموعات الثابتة (Tuples) تُستخدم عندما تريد التأكد من أن بياناتك لن تتغير بعد إنشائها.
- القواميس (Dictionaries) هي الأداة المثالية لربط البيانات ببعضها البعض باستخدام مفاتيح واضحة، مثل تخزين معلومات عن كائن معين.
- الجمع بين هذه الهياكل (مثل قائمة من القواميس) هو أسلوب قوي جدًا لتنظيم البيانات المعقدة في برامجك.
ماذا بعد؟
بعد أن أتقنت تنظيم البيانات، سننتقل في الفصل الثامن لنتعلم كيفية التعامل مع الملفات، مما سيسمح لنا بحفظ هذه الهياكل البيانية بشكل دائم على القرص الصلب وقراءتها مرة أخرى لاحقًا.