دليل لقوائم الانتظار في بايثون

دليل لقوائم الانتظار في بايثون

المُقدّمة

بدءًا من تخزين الأعداد الصحيحة البسيطة وحتى إدارة مسارات العمل المعقدة، تضع هياكل البيانات الأساس لتطبيقات قوية. من بينها، طابور غالبًا ما يظهر على أنه مثير للاهتمام وفي كل مكان. فكر في الأمر - أ خط في البنكأو انتظار دورك عند طاولة الوجبات السريعة أو تخزين المهام مؤقتًا في نظام الكمبيوتر - كل هذه السيناريوهات تتوافق مع آليات قائمة الانتظار.

يحصل الشخص الأول في الصف على الخدمة أولاً، وينضم الوافدون الجدد في النهاية. هذا مثال واقعي لقائمة الانتظار أثناء العمل!

دليل إلى قوائم الانتظار في python-01.png

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

في هذا الدليل، سنتعمق في مفهوم قوائم الانتظار، ونستكشف خصائصها، وتطبيقاتها الواقعية، والأهم من ذلك، كيفية تنفيذها واستخدامها بشكل فعال في بايثون.

ما هي بنية بيانات قائمة الانتظار؟

أثناء التنقل عبر مشهد هياكل البيانات، غالبًا ما نواجه حاويات لها قواعد مميزة لإدخال البيانات واسترجاعها. ومن بين هؤلاء، طابور تتميز بأناقتها وبساطتها.

مبدأ FIFO

قائمة الانتظار في جوهرها عبارة عن بنية بيانات خطية تلتزم بـ أول ما يدخل أولًا يخرج (FIFO) مبدأ. وهذا يعني أن العنصر الأول المضاف إلى قائمة الانتظار سيكون أول عنصر تتم إزالته. لتشبيهه بسيناريو مشابه: فكر في صف من العملاء عند شباك التذاكر. الشخص الذي يصل أولاً يحصل على تذكرته أولاً، وأي شخص يصل لاحقًا يصطف في النهاية، في انتظار دوره.

ملحوظة: قائمة الانتظار لها نهايتين - الخلفي والأمامي. يشير الجزء الأمامي إلى المكان الذي ستتم إزالة العناصر منه، ويشير الجزء الخلفي إلى المكان الذي ستتم إضافة عناصر جديدة إليه.

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

  • إدراج بقائمة الانتظار - فعل مضيفا عنصر حتى النهاية (خلفي) من قائمة الانتظار.

    دليل إلى قوائم الانتظار في python-02.png

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

    دليل إلى قوائم الانتظار في python-03.png

  • نظرة خاطفة أو الجبهة - في العديد من المواقف، من المفيد ملاحظة العنصر الأمامي فقط دون إزالته. هذه العملية تسمح لنا بالقيام بذلك.

  • فارغ - عملية تساعد في تحديد ما إذا كانت قائمة الانتظار تحتوي على أي عناصر. يمكن أن يكون هذا أمرًا بالغ الأهمية في السيناريوهات التي تتوقف فيها الإجراءات على قائمة الانتظار التي تحتوي على بيانات.

ملحوظة: في حين أن بعض قوائم الانتظار لها حجم محدود (قوائم الانتظار المحدودة)، فمن المحتمل أن تنمو قوائم أخرى طالما أن ذاكرة النظام تسمح بذلك (قوائم الانتظار غير المحدودة).

إن بساطة قوائم الانتظار وقواعد التشغيل الواضحة الخاصة بها تجعلها مثالية لمجموعة متنوعة من التطبيقات في تطوير البرمجيات، خاصة في السيناريوهات التي تتطلب معالجة منظمة ومنهجية.

ومع ذلك، فإن فهم النظرية هو مجرد خطوة أولى. بينما نمضي قدمًا، سنتعمق في الجوانب العملية، ونوضح كيفية تنفيذ قوائم الانتظار في بايثون.

كيفية تنفيذ قوائم الانتظار في بايثون – القوائم مقابل وحدة Deque مقابل وحدة الانتظار

توفر لغة Python، بمكتبتها القياسية الغنية وتركيبها سهل الاستخدام، العديد من الآليات لتنفيذ قوائم الانتظار والتعامل معها. في حين أن جميعها تخدم الغرض الأساسي لإدارة قائمة الانتظار، إلا أنها تأتي مع الفروق الدقيقة والمزايا والمزالق المحتملة. دعونا نحلل كل نهج، ونوضح آلياته وأفضل حالات الاستخدام.

ملحوظة: تحقق دائمًا من حالة قائمة الانتظار الخاصة بك قبل إجراء العمليات. على سبيل المثال، قبل إلغاء قائمة الانتظار، تحقق مما إذا كانت قائمة الانتظار فارغة لتجنب الأخطاء. وبالمثل، بالنسبة لصفوف الانتظار المحددة، تأكد من وجود مساحة قبل الدخول في قائمة الانتظار.

استخدام قوائم بايثون لتنفيذ قوائم الانتظار

يعد استخدام قوائم Python المضمنة لتنفيذ قوائم الانتظار أمرًا بديهيًا ومباشرًا. ليست هناك حاجة لمكتبات خارجية أو هياكل بيانات معقدة. ومع ذلك، قد لا يكون هذا النهج فعالاً بالنسبة لمجموعات البيانات الكبيرة. إزالة عنصر من بداية القائمة (pop(0)) يستغرق وقتًا خطيًا، مما قد يسبب مشكلات في الأداء.

ملحوظة: بالنسبة للتطبيقات التي تتطلب أداءً عاليًا أو تلك التي تتعامل مع حجم كبير من البيانات، قم بالتبديل إلى collections.deque من أجل التعقيد الزمني الثابت لكل من وضع قائمة الانتظار وإلغاء قائمة الانتظار.

لنبدأ بإنشاء قائمة لتمثيل قائمة الانتظار الخاصة بنا:

queue = []

إن عملية إضافة العناصر إلى نهاية قائمة الانتظار (الضم) ليست سوى إلحاقها بالقائمة:


queue.append('A')
queue.append('B')
queue.append('C')
print(queue) 

وأيضًا، فإن إزالة العنصر من مقدمة قائمة الانتظار (إزالة قائمة الانتظار) تعادل مجرد إزالة العنصر الأول من القائمة:


queue.pop(0)
print(queue) 

باستخدام Collections.deque لتنفيذ قوائم الانتظار

هذا النهج فعال للغاية deque يتم تنفيذه باستخدام قائمة مرتبطة بشكل مزدوج. وهو يدعم الإضافات السريعة O(1) والملوثات العضوية الثابتة من كلا الطرفين. الجانب السلبي لهذا النهج هو أنه كذلك قليلا أقل بديهية للمبتدئين.

في البداية، سنقوم باستيراد ملف deque الكائن من collections الوحدة النمطية وتهيئة قائمة الانتظار لدينا:

from collections import deque queue = deque()

الآن ، يمكننا استخدام append() طريقة لسرد العناصر و popleft() طريقة حذف العناصر من قائمة الانتظار:

تحقق من دليلنا العملي العملي لتعلم Git ، مع أفضل الممارسات ، والمعايير المقبولة في الصناعة ، وورقة الغش المضمنة. توقف عن أوامر Googling Git وفي الواقع تعلم ذلك!


queue.append('A')
queue.append('B')
queue.append('C')
print(queue) queue.popleft()
print(queue) 

استخدام لغة بايثون طابور وحدة لتنفيذ قوائم الانتظار

queue توفر الوحدة النمطية في مكتبة بايثون القياسية نهجًا أكثر تخصصًا لإدارة قائمة الانتظار، وتلبية حالات الاستخدام المختلفة:

  • SimpleQueue - قائمة انتظار FIFO الأساسية
  • LifoQueue - قائمة انتظار LIFO، وهي في الأساس مكدسة
  • طابور الأولوية - يتم ترتيب العناصر في قائمة الانتظار بناءً على الأولوية المخصصة لها

ملحوظة: اختر queue الوحدة النمطية، والتي تم تصميمها لتكون موضوع آمنة. وهذا يضمن أن العمليات المتزامنة في قائمة الانتظار لا تؤدي إلى نتائج غير متوقعة.

يعد هذا الأسلوب رائعًا لأنه مصمم بشكل صريح لعمليات قائمة الانتظار. ولكن، لكي نكون صادقين تماما، قد يكون ذلك مبالغة في السيناريوهات البسيطة.

الآن، لنبدأ باستخدام queue الوحدة عن طريق استيرادها إلى مشروعنا:

import queue

نظرًا لأننا نقوم بتنفيذ قائمة انتظار FIFO بسيطة، فسنقوم بتهيئتها باستخدام الأمر SimpleQueue() البناء:

q = queue.SimpleQueue()

يتم تنفيذ عمليات Enqueue و Dequeue باستخدام put() و get() طرق من queue وحدة:


q.put('A')
q.put('B')
q.put('C')
print(q.queue) q.get()
print(q.queue) 

ملحوظة: يمكن أن تؤدي عمليات قائمة الانتظار إلى ظهور استثناءات، والتي إذا لم تتم معالجتها، يمكن أن تؤدي إلى تعطيل تدفق التطبيق الخاص بك. لمنع ذلك، قم بإدراج عمليات قائمة الانتظار الخاصة بك try-except كتل.

على سبيل المثال، التعامل مع queue.Empty استثناء عند العمل مع queue وحدة:

import queue q = queue.SimpleQueue() try: item = q.get_nowait()
except queue.Empty: print("Queue is empty!")

ما التنفيذ الذي يجب اختياره؟

يجب أن يتوافق اختيارك لتنفيذ قائمة الانتظار في Python مع متطلبات التطبيق الخاص بك. إذا كنت تتعامل مع كمية كبيرة من البيانات أو تحتاج إلى أداء محسن، collections.deque هو خيار مقنع. ومع ذلك، بالنسبة للتطبيقات متعددة الخيوط أو عندما يتم تحديد الأولويات، فإن queue تقدم الوحدة حلولاً قوية. بالنسبة للنصوص البرمجية السريعة أو عندما تبدأ للتو، قد تكون قوائم Python كافية، ولكن كن حذرًا دائمًا من المخاطر المحتملة في الأداء.

ملحوظة: إعادة اختراع العجلة من خلال التنفيذ المخصص لعمليات قائمة الانتظار عندما توفر Python بالفعل حلولًا مدمجة قوية.
قبل صياغة حلول مخصصة، تعرف على عروض Python المضمنة مثل deque و queue وحدة. وفي أغلب الأحيان، فإنها تلبي مجموعة واسعة من المتطلبات، مما يوفر الوقت ويقلل الأخطاء المحتملة.

الغوص بشكل أعمق: مفاهيم قائمة الانتظار المتقدمة في بيثون

بالنسبة لأولئك الذين استوعبوا الآليات الأساسية لقوائم الانتظار ويتوقون إلى التعمق أكثر، تقدم بايثون عددًا كبيرًا من المفاهيم والتقنيات المتقدمة لتحسين العمليات القائمة على قوائم الانتظار وتحسينها. دعنا نكشف عن بعض هذه الجوانب المعقدة، مما يمنحك ترسانة من الأدوات للتعامل مع السيناريوهات الأكثر تعقيدًا.

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

بينما اكتشفنا سابقًا deque باعتبارها قائمة انتظار FIFO، فإنها تدعم أيضًا عمليات LIFO (آخر دخول أول خروج). يسمح لك بإلحاق أو فرقع العناصر من كلا الطرفين بتعقيد O(1):

from collections import deque dq = deque()
dq.appendleft('A') dq.append('B') dq.pop() dq.popleft() 

قائمة الانتظار الأولوية في العمل

يمكن أن يؤدي استخدام قائمة انتظار FIFO البسيطة عندما يعتمد ترتيب المعالجة على الأولوية إلى عدم الكفاءة أو نتائج غير مرغوب فيها، لذلك، إذا كان طلبك يتطلب معالجة عناصر معينة قبل العناصر الأخرى بناءً على بعض المعايير، فاستخدم طريقة PriorityQueue. وهذا يضمن معالجة العناصر بناءً على أولوياتها المحددة.

ألقِ نظرة على كيفية تحديد أولويات العناصر التي نضيفها إلى قائمة الانتظار. يتطلب هذا أن نمرر صفًا كوسيطة لـ put() طريقة. يجب أن يحتوي الصف على الأولوية كعنصره الأول والقيمة الفعلية كعنصر ثانٍ:

import queue pq = queue.PriorityQueue()
pq.put((2, "Task B"))
pq.put((1, "Task A")) pq.put((3, "Task C")) while not pq.empty(): _, task = pq.get() print(f"Processing: {task}")

وهذا سيعطينا ما يلي:

Processing: Task A
Processing: Task B
Processing: Task C

لاحظ كيف أضفنا العناصر بترتيب مختلف عن ما تم تخزينه في قائمة الانتظار. وذلك بسبب الأولويات التي حددناها في put() الطريقة عند إضافة عناصر إلى قائمة انتظار الأولوية.

تنفيذ قائمة الانتظار الدائرية

قائمة الانتظار الدائرية (أو المخزن المؤقت الحلقي) عبارة عن بنية بيانات متقدمة حيث يتم توصيل العنصر الأخير بالعنصر الأول، مما يضمن تدفقًا دائريًا. deque يمكن تقليد هذا السلوك باستخدام maxlen خاصية:

from collections import deque circular_queue = deque(maxlen=3)
circular_queue.append(1)
circular_queue.append(2)
circular_queue.append(3) circular_queue.append(4)
print(circular_queue) 

وفي الختام

قوائم الانتظار، الأساسية لكنها قوية، تجد جوهرها في مجموعة متنوعة من تطبيقات العالم الحقيقي والمشكلات الحسابية. من جدولة المهام في أنظمة التشغيل إلى إدارة تدفق البيانات في التخزين المؤقت للطباعة أو طلبات خادم الويب، فإن الآثار المترتبة على قوائم الانتظار بعيدة المدى.

تقدم Python إلى الطاولة مجموعة غنية من الأدوات والمكتبات للعمل مع قوائم الانتظار. بدءًا من قوائم الانتظار البسيطة القائمة على البرامج النصية السريعة ووصولاً إلى البرامج عالية الكفاءة deque بالنسبة للتطبيقات ذات الأداء الحيوي، تلبي اللغة حقًا مجموعة واسعة من الاحتياجات.

الطابع الزمني:

اكثر من ستاكابوز