تقنية

اقتران التتابعات zip: الطول الصارم وعلامة الغياب وهوية السجل

أربعة معرفات وثلاث قيم؛ ثلاثة أزواج افتراضية، وخطأ طول صارم، وأربعة صفوف في التقرير الممتد
ثلاث سياسات لنفس المدخلين؛ None قيمة حاضرة في الموضع الثاني.

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

الخلاصة السريعة

حدد أولًا ماذا يعني أن عنصرين يقابل أحدهما الآخر. ثم اختر سياسة الطول: توقف عند الأقصر، أو ارفض الاختلاف، أو احتفظ بمواضع الأطول مع حضور معلن.

  • zip يقرن العناصر بحسب الموضع؛ في مثال أربعة معرفات وثلاث قيم ينتج ثلاثة أزواج في الوضع الافتراضي. غياب زوج رابع لا يثبت أن المعرف الرابع لم يكن موجودًا.
  • strict=True يكشف اختلاف الأطوال أثناء الاستهلاك، وليس بمجرد إنشاء المكرر. قد تحصل على أزواج صحيحة موضعيًا قبل أن يظهر ValueError عند محاولة متابعة القراءة.
  • zip_longest يواصل إلى نهاية الأطول ويضع علامة في الموضع الذي لا يحمل عنصرًا. إذا كانت None قيمة حقيقية في البيانات، فلا نستعملها وحدها لإثبات أن عنصرًا غائب.
  • كودنا ينشئ علامة خاصة ويصدر علمي حضور إلى جانب القيم. في الصف الثاني تكون None حاضرة فعلًا، وفي الرابع قيمة الجانب الأيمن غائبة؛ العلم يفرق بينهما.
  • تساوي الأطوال لا يثبت توافق هويات السجلات. قائمتان تحملان الهويات نفسها بترتيبين مختلفين قد تنتجان أزواجًا متعارضة مع أن strict لا يعترض على الطول.
  • حذف عنصر من جهة واحدة قبل الاقتران يزيح الأزواج اللاحقة. راجع الهوية وخطوات الفرز والتصفية، ولا تستنتج أن رقم الموضع مفتاح ربط دائم بين نسخ مختلفة.
  • استعمال المكرر الواحد في الجهتين يستهلكه بالتتابع؛ لا يعطينا تلقائيًا زوجين من نسختين مستقلتين. كودنا يفترض إدخالين منتهيين نعرف علاقتهما.
  • نختبر الأطوال الفارغة والمتساوية والمختلفة، والقيم None والصفر، ونراجع تقرير الحضور بمرجع موضعي مستقل. لم ننفذ ربطًا على بيانات خدمة أو نثبت صحة معنوية لكل زوج.

ابدأ بعلاقة السجلات قبل اسم الأداة

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

توثق Python أن zip تنتج صفوفًا تضم العنصر الموافق في كل إدخال، وتتوقف افتراضيًا عند الأقصر. وهي كسولة فلا تعالج العناصر إلا أثناء القراءة. الخيار strict يرفع ValueError عند اختلاف الأطوال، بينما تملأ zip_longest المواضع الناقصة حتى نهاية الأطول. سنستخدم هذه العقود ثم نحسب نتائج أمثلة أصلية.[1][2]

قبل الحساب نكتب هل ترتيب الجهتين من المصدر نفسه وهل أجريت عليهما العمليات نفسها. إذا لم نعرف ذلك، يصبح الاقتران ناتجًا حسابيًا يحتاج مراجعة هويات، لا دليلًا أن قيمة بعينها تخص معرفًا بعينه. لا تختار الصرامة أو الحشو بدل الإجابة عن هذا السؤال؛ كل منهما يعالج طولًا، أما المعنى فيأتي من البيانات والعلاقة التي أعلنّاها.

أربعة معرفات وثلاث قيم معلنة

قائمة المعرفات في نموذجنا هي k0 ثم k1 ثم k2 ثم k3. وقائمة القيم هي 10 ثم None ثم 30. القيم بلا وحدة خارجية، وNone عنصر مدخل صراحةً لا رقمًا نقدره من سياق. اخترنا اختلاف العدد ووجود None معًا كي نفصل بين عنصر حاضر يحمل هذه القيمة وبين موضع لا يحتوي أي عنصر أصلًا.

في الوضع الافتراضي يظهر الزوج k0 مع 10، ثم k1 مع None، ثم k2 مع 30. لا يظهر زوج k3 في النتيجة لأن الجانب الآخر انتهى. إذا صدّرنا الأزواج فقط دون حفظ ملخص أطوال المدخلات، فلن يعرف قارئ الناتج هل كان الإدخال أربعة معرفات أم ثلاثة. هذا فقد لمعلومة يمكن وصفه مباشرةً من بيانات المثال.

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

الجزء المشترك ليس فحصًا للاكتمال

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

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

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

الصرامة تظهر عند استهلاك المكرر

ننشئ zip مع strict=True على المدخلين المختلفين. إنشاء الكائن لا يعطينا فورًا تقريرًا أن العددين مختلفان في تجربتنا. عند القراءة نحصل على الأزواج الثلاثة الأولى، ثم ترفع محاولة المتابعة ValueError بسبب عدم تساوي الأطوال. لا نكتب أن strict فحص كامل مسبق لمجرد أننا أضفنا الخيار إلى سطر إنشاء المكرر.

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

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

احتفظ بالطرف الأطول مع حضور واضح

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

نميز بين اسم العلامة في الرسم وبين قيمتها في الكود. سنطبع كلمة MISSING للتوضيح فقط، لكننا لا نستعمل النص نفسه علامة داخل البيانات، فقد يكون نصًا حقيقيًا في نموذج آخر. ينشئ الكود كائنًا خاصًا محليًا لا يضعه المستدعي في المدخلات، ثم يسأل عن هوية هذا الكائن بدل مساواته بقيمة عددية أو نصية.

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

None حاضرة تختلف من غياب عنصر

في صف k1 يوجد عنصر يميني فعلي قيمته None. وفي صف k3 لا يوجد عنصر يميني. لو ملأنا الغياب بقيمة None وحدها، لحمل الصفان العرض نفسه في عمود القيمة، ولن نعرف من هذا العمود وحده أيهما جاء من عنصر أصلي وأيهما أضيف لتمثيل الغياب. لذلك لا نعامل عدم وجود رقم على الشاشة دليلًا كافيًا على غياب الصف.

كودنا يكتب right_present بوصفه true عند k1، وfalse عند k3. تبقى خانة القيمة None في الاثنين عند تصدير التقرير، لكن العلم يحتفظ بالمعلومة الفارقة. يمكن للقارئ إعادة بناء القيم الأصلية بأخذ الصفوف ذات الحضور الحقيقي فقط، بما فيها None. لو اختار الصفوف ذات القيمة الصادقة بدل الحضور، فقد يحذف None أو صفرًا كانا عنصرين مقصودين.

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

None في k1 عنصر حاضر، وفي k3 حقل قيمة يعرض غيابًا مع علم حضور false
نحفظ علم الحضور مستقلًا من القيمة المرئية.
عرض الرسم بالحجم الكامل

كود أصلي يصدر الحضور والموضع

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

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

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

from itertools import zip_longest


def positional_report(left, right):
    marker = object()
    result = []
    for position, (a, b) in enumerate(
        zip_longest(left, right, fillvalue=marker)
    ):
        result.append({
            "position": position,
            "left_present": a is not marker,
            "right_present": b is not marker,
            "left": None if a is marker else a,
            "right": None if b is marker else b,
        })
    return result

الأطوال المتساوية لا تثبت تطابق الهوية

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

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

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

حذف عنصر من طرف واحد يزيح الشركاء

في نسخة سليمة نفترض المعرفات k0 وk1 وk2 والقيم 10 و20 و30 بهذا الترتيب. نحذف القيمة الوسطى 20 وحدها قبل الاقتران، فتكون القيم 10 و30. يعطينا الوضع الافتراضي k0 مع 10، ثم k1 مع 30، ولا يظهر k2. لدينا الآن زوج خاطئ بالنسبة للعلاقة الأصلية ومعرف غير مغطى، وليس مجرد صف أخير يمكن ترقيعه بلا معرفة موضع الحذف.

الحشو في النهاية سيضع غيابًا عند k2، لكنه لا يعرف أن القيمة التي كانت تخص k1 هي التي حذفت من الوسط. كل ما يراه تتابعان بطولين مختلفين. وإذا وضعنا None بدل حذف الموضع الأوسط، صار الترتيب 10 وNone و30، وعادت القيم اللاحقة إلى مواضعها الأصلية؛ هذا ناتج مختلف لأننا احتفظنا بموضع ذي معنى معلن.

لهذا نحفظ هوية مستقلة مع القيمة عندما يمكن أن تجري التصفية على طرف واحد. يمكن لسجل مثل هوية k2 وقيمة 30 أن يكشف أن القيمة الثانية في النسخة المصفاة لا تخص المعرف الثاني في النسخة الأخرى. لا تستطيع أداة تعتمد الموضع استرجاع معلومة أزيلت من الإدخال دون حقل أو سجل يربطها بالأصل.

إعادة استعمال المكرر تختلف من نسختين

نجرب مكررًا واحدًا على القيم 4 و8 و12 و16. حين نعطي الكائن نفسه طرفين، يقرأ الزوج الأول 4 ثم 8 من المسار نفسه، ويقرأ الثاني 12 ثم 16. النتيجة ليست 4 مع 4 ثم 8 مع 8 كما في إدخالين مستقلين بالقيم نفسها. هذا أثر مشاركة حالة القراءة في تجربتنا، لا اختلاف في قيم القائمة الأصلية.

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

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

كيف نعيد بناء المدخل من تقرير الحضور

من تقرير دالتنا نأخذ قيم اليسار حين يكون left_present صحيحًا، فتعود k0 إلى k3. ونأخذ قيم اليمين حين يكون right_present صحيحًا، فتعود 10 وNone و30. لا ندخل العلامة المضافة في الأصل المعاد، ولا نحذف None الحقيقية. هذا اختبار عملي أن التقرير حافظ على العناصر الفردية لهذا الإدخال المنتهي.

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

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

الإدخال المنتهي شرط المثال المحفوظ

الدالة ترجع قائمة مكتملة؛ ولذلك نستخدم تتابعات منتهية في جميع الحالات. إذا كان أحد الطرفين غير منتهٍ واصل الاقتران الممتد طلب صفوف، ولم يصبح تحويل جميع نتائجه إلى قائمة عملًا ينتهي في هذا العقد. إن احتجنا مقدمة محدودة، نحدد عددًا واضحًا من الصفوف، ونكتب أن الفحص يخص تلك المقدمة دون نهاية التيار.[2]

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

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

فحوص تفصل الطول والحضور والمعنى

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

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

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

اكتب سياسة الاقتران بجانب الناتج

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

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

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

المصادر ومتابعة القراءة

  1. Python 3.14: zip والقراءة الكسولة والطول الصارم (يفتح في نافذة جديدة)docs.python.org
  2. Python 3.14: zip_longest والحشو حتى نهاية الأطول (يفتح في نافذة جديدة)docs.python.org

أُعدّ هذا المقال بصياغة عربية أصلية بالاستناد إلى المصادر أعلاه، وهو مدخل تمهيدي إلى الموضوع. اقرأ منهجية المحتوى وحدوده.