هياكل البيانات الأساسية
درس 4 من 8
4هياكل البيانات الأساسية (Core Data Structures)
الصورة الكبيرةالقائمة والتابل والقاموس والمجموعة: ما الذي يميّز كلًا منها، ومتى يكون اختيار الهيكل الخاطئ هو سبب بطء برنامجك.
ماذا تشاهد في الرسم: القائمة صفّ خانات مرقّمة من الصفر، وappend تضيف في آخرها. والقاموس يربط كل اسم بقيمته فتصل إليها بالاسم.
خارطة هذا الجزء
المفاهيم بالترتيب، اضغط أيًّا منها للقفز إليه:
طريقان إلى نفس الفكرة
هياكل البيانات أنواع من الحاويات. القائمة رف كتب مرتّب تستطيع أن تضيف إليه وتزيل منه. التابل علبة مغلقة لا تتغير. القاموس دفتر هاتف: تبحث بالاسم فتجد الرقم فورًا. والمجموعة كيس لا يقبل التكرار. اختيار الحاوية الخطأ يجعلك تفتّش الرف كله بحثًا عن كتاب واحد.
list مرتّبة وقابلة للتعديل ويستغرق البحث فيها O(n). وtuple غير قابل للتعديل ويصلح مفتاحًا. وdict جدول تجزئة يصل للمفتاح في O(1) بالمتوسط. وset عناصر فريدة بفحص عضوية سريع وعمليات اتحاد وتقاطع. هذا الفرق هو سبب بطء برامج كثيرة.
قبل أن تبدأ
🔁الحلقات
for تمشي على عدد من القيم واحدة واحدة.
🧩الدوال
تعرف كيف تمرّر قيمة لدالة وتأخذ ناتجها.
الفكرة الجوهرية
برنامج حقيقي يتعامل مع مجموعات من القيم لا مع قيمة واحدة. القائمة والقاموس هما الهيكلان اللذان ستستخدمهما يوميًّا، فابدأ بهما قبل الجدول المقارن.
1القائمة: عدة قيم مرتّبة
القائمة تُكتب بين قوسين مربّعين. تصل إلى عنصر برقم موضعه، والعدّ يبدأ من صفر. -1 يعني الأخير، وlen يعطي عدد العناصر.
fruits = ["تفاح", "موز", "عنب"]
print(fruits[0])
print(fruits[-1])
print(len(fruits))
fruits.append("تين")
print(fruits)
append تضيف في النهاية. القائمة تتغيّر في مكانها، ولهذا طُبعت بأربعة عناصر بعد الإضافة.
2امشِ على القائمة
حلقة for تمرّ على كل عنصر بالترتيب، كما مرّت على أرقام range في الجزء السابق. ومع الأرقام عندك دوال جاهزة: المجموع والأكبر والفرز.
fruits = ["تفاح", "موز"]
for fruit in fruits:
print("-", fruit)
numbers = [4, 9, 2]
print(sum(numbers), max(numbers), sorted(numbers))
sorted تعيد قائمة جديدة مرتّبة ولا تغيّر الأصل.
3القاموس: اسم يقابله قيمة
القاموس يربط مفتاحًا بقيمة، بين قوسين معقوفين. تصل إلى القيمة بالمفتاح لا برقم الموضع. وتضيف زوجًا جديدًا بنفس الصيغة.
ages = {"سارة": 21, "خالد": 19}
print(ages["سارة"])
ages["نورة"] = 25
print(ages)
print("خالد" in ages, "ريم" in ages)
in تسأل: هل هذا المفتاح موجود؟ وهي أسلم من محاولة القراءة مباشرة، لأن قراءة مفتاح غير موجود ترفع KeyError.
IndexError: list index out of range. آخر عنصر دائمًا موضعه len - 1، أو ببساطة -1.كيف يعمل، خطوة بخطوة: كيف يجد القاموس قيمة باسمها
تكتب prices["موز"].
بايثون يحوّل المفتاح إلى رقم بدالة تسمى hash.
الرقم يدلّه على الخانة مباشرة، دون أن يمرّ على البقية.
يعيد القيمة، أو يرفع KeyError إن لم يجد المفتاح.
أعمق قليلًا
أغلب البرامج الحقيقية تدور حول مجموعات من القيم، لا متغيّرات مفردة. اختيار الهيكل الصحيح يحدد كفاءة ووضوح كودك.
| الهيكل | مرتّب؟ | قابل للتعديل؟ | يسمح بتكرار العناصر؟ | الاستخدام المثالي |
|---|---|---|---|---|
List [] | نعم | نعم | نعم | قائمة عناصر قد تتغيّر (سلة مشتريات) |
Tuple () | نعم | لا | نعم | مجموعة ثابتة (إحداثية x, y) |
Set {} | لا | نعم | لا (عناصر فريدة) | عناصر بلا تكرار وعمليات تقاطع/فرق |
Dict {k: v} | نعم (من 3.7+) | نعم | المفاتيح فريدة | ربط مفتاح بقيمة (اسم ← درجة) |
## List — سلة مشتريات قابلة للتعديل
cart = ["apple", "bread"]
cart.append("milk")
print(cart) # ['apple', 'bread', 'milk']
## Tuple — إحداثية ثابتة لا تتغيّر بالخطأ
point = (3, 7)
# point[0] = 9 -> يرفع TypeError لأن Tuple غير قابل للتعديل
## Set — دمج قوائم اهتمامات صديقين بدون تكرار
alice_tags = {"python", "sql", "git"}
bob_tags = {"sql", "docker", "python"}
shared = alice_tags & bob_tags # تقاطع: {'python', 'sql'}
## Dict — ربط اسم بدرجة
grades = {"Sara": 95, "Omar": 88}
grades["Lina"] = 91 # إضافة عنصر جديد
print(grades.get("Ahmed", "غير مسجَّل")) # قيمة افتراضية آمنة بدل KeyError
اختبر فهمك: لماذا لا يمكن استخدام List كمفتاح Dictionary بينما يمكن استخدام Tuple؟
لأن مفاتيح الـ Dictionary يجب أن تكون غير قابلة للتغيير (Hashable) حتى تبقى قيمة "بصمتها" الداخلية ثابتة طوال حياتها. الـ List قابلة للتعديل فبصمتها غير مستقرة، بينما Tuple ثابتة فبصمتها مضمونة.
مثال محلول
counts = {}، ومع كل اسم counts[name] = counts.get(name, 0) + 1. get تعطي صفرًا لأول ظهور بدل KeyError.خرافة مقابل حقيقة
| الخرافة | الحقيقة |
|---|---|
| القائمة تصلح لكل شيء. | البحث عن عنصر في قائمة يمرّ عليها كلها. إذا كنت تبحث بالاسم فالقاموس أسرع بكثير، وإذا أردت التفرّد فالمجموعة set. |
| أول عنصر في القائمة رقمه 1. | رقمه 0. القائمة ذات الثلاثة عناصر أرقامها 0 و1 و2، و[3] يرفع IndexError. |
| الصف tuple قائمة ناقصة لا فائدة منه. | لا يتغيّر بعد إنشائه، وهذا بالضبط ما تريده لقيم ثابتة مثل إحداثيات، ويصلح مفتاحًا في القاموس. |
جرّب بنفسك
عندك items = ["a", "b", "c"]. ما ناتج items[len(items)]؟
len(items) يساوي 3، والمواضع المتاحة 0 و1 و2 فقط، فالموضع 3 خارج القائمة. لتصل إلى الأخير اكتب items[-1] أو items[len(items) - 1]. هذا هو خطأ «الواحد الزائد» الذي سيلاحقك في كل اللغات.
بطاقات مراجعة سريعة
dict.get(key, default) بدل dict[key] مباشرة.تحقق من فهمك
ما الفرق بين القائمة والمجموعة set؟
القائمة مرتّبة وتقبل التكرار، والمجموعة بلا ترتيب ولا تقبل التكرار.
كيف تقرأ قيمة من قاموس دون أن يتوقف البرنامج إن غاب المفتاح؟
بـd.get("key") التي تعيد None، أو d.get("key", 0) بقيمة بديلة.
ماذا يطبع [1, 2, 3][-1]؟
3؛ الرقم السالب يعدّ من آخر القائمة.
مصادر هذا الدرس
مرتّبة بالنوع. ابدأ بالتوثيق الرسمي إذا تبي المرجع الدقيق، وبالمقال أو الفيديو إذا تبي الشرح أولًا.
توثيق رسمي2
خلّصت الدرس؟
سجّل الدخول ليُحفظ تقدّمك.