فهم قوائم الانتظار (Queues) في بايثون: دليل المبتدئين الشامل

مرحباً بك في درس اليوم! سنتعرف على أحد أهم هياكل البيانات الأساسية التي ستواجهها في البرمجة، وهي قوائم الانتظار أو Queues. تخيل أنك تقف في طابور لشراء قهوة ☕️. أول شخص يصل هو أول من يخدم ويغادر. هذا بالضبط المبدأ الذي تعمل به قوائم الانتظار في البرمجة!


🤔 ما هي قائمة الانتظار (Queue)؟

قائمة الانتظار (Queue) هي هيكل بيانات خطي يتبع مبدأ أول ما يدخل هو أول ما يخرج (FIFO - First-In, First-Out). هذا يعني أن العنصر الذي تمت إضافته أولاً إلى القائمة سيكون هو أول عنصر يتم إزالته منها.

العمليات الأساسية لقائمة الانتظار:

  • الإضافة (Enqueue): إضافة عنصر جديد إلى نهاية قائمة الانتظار.
  • الإزالة (Dequeue): إزالة العنصر الموجود في مقدمة قائمة الانتظار.
  • معاينة المقدمة (Peek/Front): الحصول على قيمة العنصر في المقدمة دون إزالته.
  • الفحص (isEmpty): التحقق مما إذا كانت قائمة الانتظار فارغة.

💡 تنفيذ قائمة انتظار من الصفر (Custom Queue)

فهم كيفية بناء هيكل البيانات بنفسك هو أفضل طريقة لإتقان مبادئه. دعونا ننشئ صنف Queue خاص بنا.

1. هيكل الصنف الأساسي

سنبدأ بإنشاء الصنف وتهيئة القائمة الداخلية التي ستخزن البيانات.

class Queue:
    def __init__(self):
        # قائمة داخلية لتخزين عناصر قائمة الانتظار
        self.items = []

    def is_empty(self):
        """للتحقق مما إذا كانت قائمة الانتظار فارغة"""
        return len(self.items) == 0

💻 تنفيذ العمليات الأساسية على قوائم الانتظار

الآن سنقوم بتطبيق العمليات الرئيسية: الإضافة (Enqueue)، والإزالة (Dequeue)، والمعاينة (Peek).

1. إضافة عناصر (Enqueue)

تتم الإضافة دائمًا إلى نهاية قائمة الانتظار.

class Queue:
    # ... (الكود السابق)

    def enqueue(self, item):
        """إضافة عنصر جديد إلى نهاية قائمة الانتظار"""
        self.items.append(item)  # نستخدم append للإضافة إلى النهاية
        print(f"تمت إضافة: {item}")  # تعليق: طباعة تأكيد للإضافة

2. إزالة عنصر (Dequeue)

تتم الإزالة دائمًا من مقدمة قائمة الانتظار (العنصر الأقدم).

class Queue:
    # ... (الكود السابق)

    def dequeue(self):
        """إزالة العنصر من مقدمة قائمة الانتظار وإرجاعه"""
        if self.is_empty():
            print("تحذير: قائمة الانتظار فارغة!")
            return None  # تعليق: إرجاع قيمة فارغة إذا كانت القائمة فارغة
        return self.items.pop(0)  # نستخدم pop(0) لإزالة العنصر الأول

3. معاينة العنصر الأمامي (Peek)

ننظر إلى العنصر الموجود في المقدمة دون إزالته من القائمة.

class Queue:
    # ... (الكود السابق)

    def peek(self):
        """إرجاع العنصر الموجود في مقدمة قائمة الانتظار دون إزالته"""
        if self.is_empty():
            print("قائمة الانتظار فارغة، لا يوجد عنصر للمعاينة.")
            return None
        return self.items[0]  # تعليق: الوصول للعنصر الأول مباشرة

    def size(self):
        """إرجاع عدد العناصر في قائمة الانتظار"""
        return len(self.items)

🧪 مثال تطبيقي: محاكاة طابور خدمة العملاء

لنرى كيف يعمل تنفيذنا الجديد في مثال حيوي.

# إنشاء كائن من قائمة الانتظار الخاصة بنا
customer_queue = Queue()

# إضافة عملاء إلى قائمة الانتظار
print("--- وصول العملاء ---")
customer_queue.enqueue("أحمد")
customer_queue.enqueue("سارة")
customer_queue.enqueue("خالد")

print(f"\nالعنصر التالي للخدمة: {customer_queue.peek()}")
print(f"عدد العملاء في الطابور: {customer_queue.size()}")

# خدمة العملاء
print("\n--- بدء الخدمة ---")
served = customer_queue.dequeue()
print(f"تم خدمة العميل: {served}")
print(f"بعد الخدمة، التالي هو: {customer_queue.peek()}")

# إضافة عميل جديد أثناء الخدمة
print("\n--- وصول عميل جديد ---")
customer_queue.enqueue("نورة")
print(f"عدد العملاء الآن: {customer_queue.size()}")

# خدمة باقي العملاء
print("\n--- استكمال الخدمة ---")
while not customer_queue.is_empty():
    served = customer_queue.dequeue()
    print(f"تم خدمة: {served}")

print(f"\nالحالة النهائية. هل الطابور فارغ؟ {customer_queue.is_empty()}")

🛠️ استخدام القوائم المدمجة (List) مباشرة

للمقارنة، هذه هي الطريقة السريعة لاستخدام القائمة (list) كقائمة انتظار دون إنشاء صنف مخصص.

# إنشاء قائمة انتظار فارغة
my_queue = []

# إضافة عناصر (Enqueue)
my_queue.append("Task 1")
my_queue.append("Task 2")

# إزالة عنصر (Dequeue)
next_task = my_queue.pop(0)  # تعليق: إزالة العنصر الأول
print("المهمة التالية:", next_task)

# معاينة (Peek)
if my_queue:  # تعليق: اختصار للتحقق من أن القائمة ليست فارغة
    print("العنصر في المقدمة:", my_queue[0])

⚙️ استخدام وحدة queue الرسمية في بايثون

بينما القوائم (list) وتنفيذنا المخصص يعملان، إلا أن استخدام pop(0) غير فعال للبيانات الضخمة. توفر بايثون وحدة queue مع صنف Queue الأمثل.

import queue

# إنشاء كائن Queue
my_better_queue = queue.Queue()

# الإضافة (Enqueue)
my_better_queue.put("ملف 1")
my_better_queue.put("ملف 2")

# الإزالة (Dequeue)
processed_item = my_better_queue.get()
print("تم معالجة:", processed_item)

# للتحقق من الفَراغ
print("هل فارغة؟", my_better_queue.empty())

ملاحظة: queue.Queue مصمم للبرمجة المتوازية (thread-safe). للمشاريع التعليمية والبسيطة، التنفيذ المخصص أو استخدام القوائم مباشرة كافٍ.


📋 ملخص سريع ومقارنة

  • المبدأ: FIFO (أول ما يدخل هو أول ما يخرج).
  • التنفيذ المخصص: بناء صنف Queue بأساليب enqueue()، dequeue()، و peek() يعمق الفهم.
  • باستخدام List مباشرة: append() للإضافة، pop(0) للإزالة (انتبه للأداء).
  • الوحدة الرسمية queue: queue.Queue هو الحل الأمثل والآمن للتطبيقات الحقيقية والمعقدة.