اذهب إلى المحتوى

البحث في الموقع

المحتوى عن 'دليل تعلم بايثون'.

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المحتوى


التصنيفات

  • الإدارة والقيادة
  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • السلوك التنظيمي في المؤسسات
  • عالم الأعمال
  • التجارة والتجارة الإلكترونية
  • نصائح وإرشادات
  • مقالات ريادة أعمال عامة

التصنيفات

  • مقالات برمجة عامة
  • مقالات برمجة متقدمة
  • PHP
    • Laravel
    • ووردبريس
  • جافاسكربت
    • لغة TypeScript
    • Node.js
    • React
    • Vue.js
    • Angular
    • jQuery
    • Cordova
  • HTML
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • SQL
  • لغة C#‎
    • ‎.NET
    • منصة Xamarin
  • لغة C++‎
  • لغة C
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • لغة Rust
  • برمجة أندرويد
  • لغة R
  • الذكاء الاصطناعي
  • صناعة الألعاب
  • سير العمل
    • Git
  • الأنظمة والأنظمة المدمجة

التصنيفات

  • تصميم تجربة المستخدم UX
  • تصميم واجهة المستخدم UI
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب GIMP
    • كريتا Krita
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • نصائح وإرشادات
  • مقالات تصميم عامة

التصنيفات

  • مقالات DevOps عامة
  • خوادم
    • الويب HTTP
    • البريد الإلكتروني
    • قواعد البيانات
    • DNS
    • Samba
  • الحوسبة السحابية
    • Docker
  • إدارة الإعدادات والنشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
    • ريدهات (Red Hat)
  • خواديم ويندوز
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • شبكات
    • سيسكو (Cisco)

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • استسراع النمو
  • المبيعات
  • تجارب ونصائح
  • مبادئ علم التسويق

التصنيفات

  • مقالات عمل حر عامة
  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • العمل الحر المهني
    • العمل بالترجمة
    • العمل كمساعد افتراضي
    • العمل بكتابة المحتوى

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
    • بريستاشوب
    • أوبن كارت
    • دروبال
  • الترجمة بمساعدة الحاسوب
    • omegaT
    • memoQ
    • Trados
    • Memsource
  • برامج تخطيط موارد المؤسسات ERP
    • تطبيقات أودو odoo
  • أنظمة تشغيل الحواسيب والهواتف
    • ويندوز
    • لينكس
  • مقالات عامة

التصنيفات

  • آخر التحديثات

أسئلة وأجوبة

  • الأقسام
    • أسئلة البرمجة
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات

التصنيفات

  • كتب ريادة الأعمال
  • كتب العمل الحر
  • كتب تسويق ومبيعات
  • كتب برمجة
  • كتب تصميم
  • كتب DevOps

ابحث في

ابحث عن


تاريخ الإنشاء

  • بداية

    نهاية


آخر تحديث

  • بداية

    نهاية


رشح النتائج حسب

تاريخ الانضمام

  • بداية

    نهاية


المجموعة


النبذة الشخصية

  1. كتابة برنامج يطبع العبارة "مرحبا بالعالم!‎" وتنفيذه هو أحد التقاليد الأساسية في مجال البرمجة، ويشكل أول برنامج بسيط وكامل للمبتدئين، وكذلك يُعدُّ اختبارًا لأنظمة وبيئات البرمجة، كما يعطي البرنامج فكرة عن الصياغة الأساسية للغات البرمجة. ستتعلم في هذه المقالة كيفية كتابة برنامج "مرحبا بالعالم!" في بايثون 3. المتطلبات الأساسية يجب أن يكون لديك بايثون 3 مثبتًا، بالإضافة إلى إعداد بيئة برمجة محلية على حاسوبك. إن لم تكن قد ثبَّت بايثون وأعددت البيئة البرمجة، فعد إلى الدرس السابق واتبع الخطوات اللازمة لإنجاز ذلك قبل إكمال هذا الدرس. مرحلة كتابة البرنامج لكتابة البرنامج "مرحبا بالعالم!‎"، سننشئ ملفًا جديدًا باسم hello.py، ثم نفتحه بمحرر نصوص لسطر الأوامر، (مثل nano الموجود على أنظمة لينكس): nano hello.py إن لم يكن لديك المُحرِّر nano، فأنشئ الملف hello.py وافتحه باستعمال أي محرِّر موجود على نظام التشغيل لديك (مثل المفكرة على أنظمة ويندوز). بمجرد فتح الملف النصي في نافذة الطرفية (أو محرِّر النصوص)، سنقوم بكتابة برنامجنا فيه وذلك بكتابة التعليمة البرمجية التالية: print("مرحبا بالعالم!‎") دعنا نشرّح هذه التعليمة. print()‎ هي دالة (function) تخبر الحاسوب بتنفيذ إجراء ما. نعرف أنَّها دالة لأنها تستخدم الأقواس. تطلب print()‎ من بايثون أن يعرض أو يخرج ما نضعه بين القوسين. بشكل افتراضي، ستُعرَض المُخرجات في نافذة الطرفية عند تنفيذ البرنامج. بعض الدوال، مثل print()‎، هي دوال مُدمجة (built-in) في بايثون بشكل افتراضي. هذه الدوال المدمجة متوفرة لنا دائمًا لاستخدامها في برامجنا. يمكننا أيضًا تعريف دوالنا الخاصة وبناءها من عناصر أخرى. داخل قوسي الدالة print()‎ توجد سلسلة من الأحرف - مرحبا بالعالم!‎‎ - محاطة بعلامتي اقتباس. وهذه الأحرف الموضوعة بين علامات الاقتباس تُسمى سلاسل نصية (strings) وهي أحد أنواع البيانات التي سنتعرف عليها لاحقًا. بعد الانتهاء من كتابة البرنامج، يمكنك الخروج من nano عن طريق الضغط على ctrl+x، وعندما يُطلب منك حفظ الملف، اضغط على y. بعد الخروج من المُحرِّر nano، ستعود إلى نافذة الطرفية. دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن مرحلة تنفيذ البرنامج بعد الانتهاء من كتابة برنامج "مرحبا بالعالم!‎"، فنحن جاهزون لتنفيذه. سنستخدم الأمر python3 معقوبًا باسم ملف البرنامج: python3 hello.py سيعرض برنامج hello.py الذي أنشأته للتو المخرجات التالية على نافدة الطرفية: مرحبا بالعالم!‎ دعني أشرح لك ما فعله البرنامج بمزيد من التفصيل. في الشيفرة البرمجية، مررنا السلسلة النصية مرحبا بالعالم!‎‎ إلى الدالة print()‎ التي وظيفتها طباعة ما يُمرَّر إليها على مجرى الخرج (نافذة الطرفية في حالتنا هذه). في هذا المثال، السلسلة النصية مرحبا بالعالم!‎‎ تُسمى أيضًا وسيطًا (argument)، نظرًا لأنها قيمة مُمرّرة إلى دالة. علامتا الاقتباس اللتان تحيطان بالسلسلة النصية مرحبا بالعالم!‎‎ لن تُطبعا على الشاشة لأنهما تُستخدمان لإعلام بايثون بأنّهما تحتويان على سلسلة نصية. تُحدِّد علامتا الاقتباس مُبتدأ السلسلة النصية ومنتهاها. بعد تنفيذ البرنامج، يمكنك الآن التأكد من أنّك نجحت في تثبيت بايثون 3، وأنّ البرنامج مكتوب ومُصَاغ صياغةً صحيحة. الخلاصة تهانينا! لقد أتممت كتابة أول برنامج لك، برنامج "مرحبا بالعالم!‎"، في بايثون 3. من الآن وصاعدًا، يمكنك استخدام الدالة print()‎ لعرض أية عبارات نصية أو قيم أخرى، ويمكنك أيضًا إنشاء ملفات برامج جديدة وتنفيذها بالطريقة ذاتها. لتعلم المزيد حول لغة بايثون، ننصحك بمتابعة سلسلة مقالات عن كيفية البرمجة في بايثون 3، والتي تأخذك خطوةً بخطوة لتعلم كيفية تثبيت بايثون وإعداد بيئة البرمجة، إضافةً إلى المفاهيم الأساسيات للغة، وأنواع البيانات والدوال والأصناف والبرمجة الكائنية وغيرها من المفاهيم؛ كما وفرنا دورةً تعليمية شاملةً حول تعلم التطوير واحترافه عن طريق لغة بايثون، وذلك انطلاقًا من الصفر إلى الاحتراف، وهذا في دورة تطوير تطبيقات باستخدام لغة بايثون، التي تمكّنك من تعلّم التطوير بلغة بايثون للتطبيقات ولمواقع الويب بالاعتماد على النظريات السليمة والتطبيق العملي والدعم المباشر. ترجمة -وبتصرّف- للمقال How To Write Your First Python 3 Program لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة اللاحقة: كيفية استخدام سطر أوامر بايثون التفاعلي المقالة السابقة: كيفية تثبيت بايثون 3 وإعداد بيئتها البرمجية المرجع الشامل إلى تعلم لغة بايثون تعرف على أبرز مميزات لغة بايثون كتاب البرمجة بلغة بايثون
  2. بعد أن أخذت فكرة عامة عن لغة البرمجة بايثون وتعرف على تاريخها والفرق بين أهم إصدارين لها، الإصدار 2 و الإصدار 3 في الدرس السابق، ستتعلم في هذا الدرس كيفية تثبيت بايثون على نظام تشغيلك وإعداد البيئة البرمجية اللازمة لكتابة البرامج وتنفيذها خلال رحلتك التعليمية هذه. فهرس هذا الدرس -لسهولة الانتقال إلى نظام التشغيل الذي تريده- هو: كيفية تثبيت بايثون 3 وإعداد بيئة برمجية على أوبنتو 16.04 كيفية تثبيت بايثون 3 وإعداد بيئة برمجية على أوبنتو 18.04 كيفية تثبيت بايثون 3 وإعداد بيئة برمجية على ديبيان 8 كيفية تثبيت بايثون 3 وإعداد بيئته البرمجية على CentOS 7 كيفية تثبيت بايثون 3 وإعداد بيئته البرمجية على macOS كيفية تثبيت بايثون 3 وإعداد بيئته البرمجية على ويندوز 10 الأمور التي سنسلط الضوء عليها هي: تثبيت بايثون 3 على نظام تشغيلك ثم إعداد بيئته البرمجية. تتمثل عملية تهيئة البيئة البرمجية بإعداد بيئة وهمية تمكِّنك من إنشاء مساحة معزولة في حاسوبك مخصصة لمشاريع بايثون، مما يعني أنَّ كل مشروع تعمل عليه يملك مجموعة من الاعتماديات (dependencies) والتي لن تؤثِّر على غيرها من المشاريع. يوفِّر لنا ضبط بيئةٍ برمجيةٍ تحكمًا أكبر بمشاريع بايثون وإمكانية التعامل مع إصداراتٍ مختلفةٍ من حزم بايثون وهذا مهمٌ كثيرًا عندما تتعامل مع الحزم الخارجية. بعد تثبيت بايثون وإكمال إعداد البيئة الوهمية، سنعلمك كيفية إنشاء برنامجٍ بسيطٍ يعرض العبارة «Hello World!‎» (أهلًا بالعالم!) الشهيرة، وبهذا سنتحقق من عمل البيئة عملًا صحيحًا، وستصبح آنذاك طريقة إنشاء برامج بايثون وتنفيذها مألوفةً لديك مما يمهد الطريق لكتابة وتنفيذ مشاريع بايثون اللاحقة. اختر مما يلي الدرس الخاص بنظام تشغيل حاسوبك (حاولنا شمل أشهر أنظمة التشغيل، لينكس وويندوز وماك) وانتقل إليه لاتباع الخطوات اللازمة لتنفيذ ما سبق. كيفية تثبيت بايثون 3 وإعداد بيئة برمجية في أوبنتو 16.04 أوبنتو هي إحدى أشهر توزيعات نظام التشغيل لينكس. تأتي أنظمة لينكس غالبًا محملةً بنسخة بايثون مثبتة وجاهزة للعمل. مع ذلك، لا يكون الإصدار المُثبَّت مطابق للإصدار الذي نريده في بعض الأحيان. سيأخذ هذا الدرس بيدك لتعليمك كيفية تثبيت الإصدار 3 من بايثون على أوبنتو 16.04 مع ضبط بيئته البرمجية. سيشرح لك خطوةً بخطوة عملية تثبيت بايثون 3 على جهازك المحلي وضبط البيئة البرمجية عبر سطر الأوامر. صحيح أن هذه الخطوات مخصصة للإصدار 16.04 من أوبنتو، إلا أنه يمكن الاستفادة منها في التوزيعات الأخرى المعتمدة على ديبيان. كيفية تثبيت بايثون 3 وإعداد بيئة برمجية على أوبنتو 18.04 أوبنتو هي إحدى أشهر توزيعات نظام التشغيل لينكس والذي يحوي نسخة بايثون مثبَّتةً وجاهزةً للعمل. مع ذلك، لا يكون الإصدار المُثبَّت مطابقًا للإصدار الذي نريده في بعض الأحيان. سيرشدك هذا الدرس لكيفية تثبيت الإصدار 3 من بايثون على أوبنتو 18.04 مع ضبط بيئته البرمجية. سيشرح لك خطوةً بخطوة عملية تثبيت بايثون 3 على جهازك المحلي وضبط البيئة البرمجية عبر سطر الأوامر. صحيح أن هذه الخطوات مخصصة للإصدار 18.04 من أوبنتو، إلا أنه يمكن الاستفادة منها في التوزيعات الأخرى المعتمدة على ديبيان. كيفية تثبيت بايثون 3 وإعداد بيئة برمجية على ديبيان 8 ديبيان هي إحدى أشهر توزيعات نظام التشغيل لينكس أيضًا. تكون بايثون مثبتة في معظم أنظمة التشغيل لينكس ولكن لا يكون الإصدار المُثبَّت مطابقًا للإصدار الذي نريده في بعض الأحيان. سيرشدك هذا المقال خطوةً بخطوة لتثبيت بايثون 3 على جهازك المحلي ذي نظام التشغيل ديبيان 8 وضبط البيئة البرمجية عبر سطر الأوامر. صحيح أن هذه الخطوات مخصصة للإصدار 8 من ديبيان، إلا أنه يمكن الاستفادة منها في التوزيعات الأخرى المعتمدة على ديبيان. كيفية تثبيت بايثون 3 وإعداد بيئته البرمجية على CentOS 7 CentOS هي إحدى توزيعات نظام التشغيل لينكس أيضًا. تأتي أنظمة لينكس غالبًا محملةً بنسخة بايثون مثبتة وجاهزة للعمل. مع ذلك، لا يكون الإصدار المثبت مطابق للإصدار الذي نريده. سيأخذ هذا الدرس بيدك لتعليمك كيفية تثبيت الإصدار 3 من بايثون على CentOS 7 مع ضبط بيئته البرمجية واختبارها عبر سطر الأوامر. كيفية تثبيت بايثون 3 وإعداد بيئته البرمجية على macOS إن كان نظام تشغيلك هو macOS، فهذا الدرس هو لك. ستتلعم فيه خطوةً بخطوة كيفية تثبيت بايثون 3 على macOS، وتثبيت بيئتها البرمجية عبر سطر الأوامر، ولا تقلق إن لم يكن لديك أدنى فكرة عن استعمال سطر الأوامر. بدايةً، يشرح المقال كيفية تثبيت Xcode -هي بيئة تطوير متكاملة (IDE)- ثم تثبيت وإعداد Homebrew استعدادًا لتثبيت بايثون 3. ينتقل بعدها إلى إعداد البيئة الوهمية وإنشاء وتنفيذ برنامج بسيط (أشرنا إليه في آنفًا). كيفية تثبيت بايثون 3 وإعداد بيئته البرمجية على ويندوز 10 إن كنت من مستخدمي نظام التشغيل الشهير ويندوز، فقد أتيت إلى الدرس الصحيح. سيُرشِدُك هذا الدرس خطوةً بخطوة إلى تثبيت بايثون 3 في ويندوز 10، وتهيئة بيئته البرمجية عبر سطر الأوامر. الخطوة الأولى لإنجاز ذلك هي فتح وإعداد PowerShell -برنامج من ميكروسوفت يوفر واجهة سطر الأوامر- ثم تثبيت مدير الحزم Chocolatey وذلك من أجل تثبيت بايثون 3. يُعرِّج بعدها على إعداد البيئة الوهمية وإنشاء برنامج بسيط وتنفيذه (أشرنا إليه في آنفًا). اقرأ أيضًا الدرس التالي: كيف تكتب أول برنامج لك في بايثون 3 الدرس السابق: اعتبارات عملية للاختيار ما بين بايثون 2 و بايثون 3 المرجع الشامل إلى تعلم لغة بايثون تعرف على أبرز مميزات لغة بايثون كتاب البرمجة بلغة بايثون
  3. يبدو نوع البيانات tuple في بايثون كما يلي: coral = ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral') tuple (صف وتُجمَع إلى صفوف) هي بنية بيانات تُمثِّل سلسلة مرتبة من العناصر غير القابلة للتبديل، وبالتالي لا يمكن تعديل القيم الموجودة فيها. يستعمل نوع البيانات tuple لتجميع البيانات، فكل عنصر أو قيمة داخل tuple تُشكِّل جزءًا منه. توضع القيم داخل نوع البيانات tuple بين قوسين ( ) ويُفصَل بينها بفاصلة ,، وتبدو القيم الفارغة كما يلي coral = ()‎، لكن إذا احتوى نوع البيانات tuple على قيم –حتى لو كانت قيمةً واحدةً فقط– فيجب وضع فاصلة فيه مثل coral = ('blue coral',). إذا استخدمنا الدالة print()‎ على tuple، فسنحصل على الناتج الآتي الذي يُبيّن أنَّ القيمة الناتجة ستوضع بين قوسين: print(coral) ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral') عند التفكير بنوع tuple وغيره من بنى البيانات التي تُعبَر من أنوع «المجموعات» (collections)، فمن المفيد أن تضع ببالك مختلف المجموعات الموجودة في حاسوبك: تشكيلة الملفات الموجودة عندك، وقوائم التشغيل للموسيقى، والمفضلة الموجودة في متصفحك، ورسائل بريدك الإلكتروني، ومجموعة مقاطع الفيديو التي تستطيع الوصول إليها من التلفاز، والكثير. نوع tuple شبيه بالقوائم (lists)، لكن القيم الموجودة فيه لا يمكن تعديلها، وبسبب ذلك، فأنت تخبر الآخرين أنَّك لا تريد إجراء أيّة تعديلات على هذه السلسلة من القيم عندما تستعمل tuple في شيفرتك. إضافةً إلى ما سبق، ولعدم القدرة على تعديل القيم، فسيكون أداء برنامجك أفضل، حيث ستُنفَّذ الشيفرة بشكل أسرع إذا استعملتَ tuple بدلًا من القوائم (lists). فهرسة نوع البيانات tuple يمكن الوصول إلى كل عنصر من عناصر tuple بمفرده لأنَّه سلسلة مرتبة من العناصر، وذلك عبر الفهرسة. وكل عنصر يرتبط برقم فهرس، الذي هو عدد صحيح يبدأ من الفهرس 0. لمثال coral السابق، ستبدو الفهارس والقيم المرتبطة بها كالآتي: ‘blue coral’ ‘staghorn coral’ ‘pillar coral’ ‘elkhorn coral’ 0 1 2 3 العنصر الأول الذي يُمثِّل السلسلة النصية 'blue coral' تبدأ بالفهرس 0، وتنتهي القائمة بالفهرس رقم 3 المرتبط بالقيمة 'elkhorn coral'. ولأن كل عنصر من عناصر tuple له رقم فهرس مرتبط به، فسنتمكن من الوصول إلى عناصره فرادى. يمكننا الآن الوصول إلى عنصر معيّن في tuple عبر استخدام رقم الفهرس المرتبط به. print(coral[2]) pillar coral تتراوح قيم الفهارس في المتغير coral من 0 إلى 3 كما هو ظاهر في الجدول السابق، لذا يمكننا استدعاء العناصر الموجودة فيه فرادى كما يلي: coral[0] coral[1] coral[2] coral[3] إذا حاولنا استدعاء المتغير coral مع رقم فهرس أكبر من 3، فستظهر رسالة خطأ تشير إلى أنَّ الفهرس خارج المجال: print(coral[22]) IndexError: tuple index out of range إضافةً إلى أرقام الفهارس الموجبة، يمكننا أيضًا الوصول إلى الفهارس باستخدام رقم فهرس سالب، وذلك بالعد بدءًا من نهاية قائمة العناصر وسيرتبط آخر عنصر بالفهرس ‎-1، وهذا مفيدٌ جدًا إذا كان لديك متغير من النوع tuple وكان يحتوي عناصر كثيرة وأردتَ الوصول إلى أحد عناصره انطلاقًا من النهاية. ففي مثالنا السابق عن coral، إذا أردنا استخدام الفهارس السالبة فالناتج كالآتي: ‘elkhorn coral’ ‘pillar coral’ ‘staghorn coral’ ‘blue coral’ -1 -2 -3 -4 إذا أردنا طباعة العنصر 'blue coral' باستخدام الفهارس السالبة، فستبدو التعليمة كما يلي: print(coral[-4]) blue coral يمكننا إضافة العناصر النصية الموجودة في tuple إلى السلاسل النصية الأخرى باستخدام المعامل +: print('This reef is made up of ' + coral[1]) This reef is made up of staghorn coral استطعنا في المثال السابق إضافة عنصر موجود في الفهرس 1 مع السلسلة النصية 'This reef is made up of '، ويمكننا أيضًا استخدام المعامل + لإضافة بنيتَي tuple معًا. الخلاصة: يمكننا الوصول إلى كل عنصر من عناصر tuple على حدة باستخدام أرقام الفهارس (الموجبة أو السالبة) المرتبطة بها. تقطيع قيم tuple يمكننا استخدام الفهارس للوصول إلى عدِّة عناصر من tuple، أما التقطيع فيسمح لنا بالوصول إلى عدِّة قيم عبر إنشاء مجال من أرقام الفهارس المفصولة بنقطتين رأسيتين [x:y]. لنقل أننا نريد عرض العناصر الموجودة في وسط المتغير coral، يمكننا فعل ذلك بإنشاء قطعة جديدة: print(coral[1:3]) ('staghorn coral', 'pillar coral') عند إنشاء قطعة جديدة –كما في المثال السابق– فيمثِّل أوّل رقم مكان بدأ القطعة (متضمنةً هذا الفهرس)، ورقم الفهرس الثاني هو مكان نهاية القطعة (دون تضمين هذا الفهرس بالقطعة)، وهذا هو السبب وراء عرض المثال السابق للقيم المرتبطة بالعناصر الموجودة في الفهرسين 1 و 2. إذا أردتَ تضمين إحدى نهايتَي القائمة، فيمكنك حذف أحد الأرقام في التعبير tuple[x:y]، فمثلًا، لنقل أننا نريد عرض أوّل ثلاثة عناصر من coral، والتي هي 'blue coral' و 'staghorn coral' و 'pillar coral'، فيمكننا فعل ذلك كالآتي: print(coral[:3]) ('blue coral', 'staghorn coral', 'pillar coral') المثال السابق عرض العناصر من بداية القائمة وتوقف قبل العنصر ذي الفهرس 3. لتضمين كل العناصر الموجودة في نهاية tuple، فيمكننا عكس التعبير السابق: print(coral[1:]) ('staghorn coral', 'pillar coral', 'elkhorn coral') يمكننا استخدام الفهارس السالبة أيضًا عند التقطيع، كما فعلنا مع أرقام الفهارس الموجبة: print(coral[-3:-1]) print(coral[-2:]) ('staghorn coral', 'pillar coral') ('pillar coral', 'elkhorn coral') هنالك معاملٌ إضافيٌ يمكننا استعماله ويسمى «الخطوة»، ويُشير إلى عدد العناصر التي يجب تجاوزها بعد الحصول على أوّل عنصر من القائمة. حذفنا في جميع أمثلتنا السابقة معامل الخطوة، حيث القيمة الافتراضية له في بايثون هي 1، لذا سنحصل على جميع العناصر الموجودة بين الفهرسَين المذكورين. شكل هذا التعبير العام هو tuple[x:y:z]، إذ يُشير المعامل z إلى الخطوة. لنُنشِئ قائمةً أكبر، ثم نقسِّمها، ونعطيها القيمة 2 كخطوة: numbers = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) print(numbers[1:11:2]) (1, 3, 5, 7, 9) التعبير numbers[1:11:2] سيطبع القيم الموجودة بين رقمين الفهرسين 1 (بما في ذلك العنصر المرتبط بالفهرس 1) و 11 (دون تضمين ذلك العنصر)، ومن ثم ستخبر قيمةُ الخطوة 2 البرنامجَ أنَّ يتخطى عنصرًا بين كل عنصرين. يمكننا حذف أوّل معاملين واستخدام معامل الخطوة بمفرده بتعبيرٍ برمجيٍ من الشكل tuple[::z]: print(numbers[::3]) (0, 3, 6, 9, 12) طبعنا في المثال السابق عناصر numbers بعد ضبط قيمة الخطوة إلى 3، وبالتالي سيتم تخطي عنصرين. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 الخلاصة: تقطيع tuples باستخدام أرقام الفهارس الموجبة والسالبة واستعمال معامل الخطوة يسمح لنا بالتحكم بالناتج الذي نريد عرضه. إضافة بنى tuple إلى بعضها يمكن أن نُضيف بنى tuple إلى بعضها أو أن «نضربها» (multiply)، تتم عملية الإضافة باستخدام المعامل +، أما عملية الضرب فباستخدام المعامل *. يمكن أن يُستخدَم المعامل + لإضافة بنيتَي tuple أو أكثر إلى بعضها بعضًا. يمكننا إسناد القيم الموجودة في بنيتَي tuple إلى بنية جديدة: coral = ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral') kelp = ('wakame', 'alaria', 'deep-sea tangle', 'macrocystis') coral_kelp = (coral + kelp) print(coral_kelp) الناتج: ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral', 'wakame', 'alaria', 'deep-sea tangle', 'macrocystis') وصحيحٌ أنَّ المعامل + يمكنه إضافة بنى tuple إلى بعضها، لكن يمكن أن يستعمل لإنشاء بنية tuple جديدة ناتجة عن جمع بنى أخرى، لكن لا يمكنه تعديل بنية tuple موجودة مسبقًا. أما المعامل * فيمكن استخدامه لضرب بنى tuple، فربما تريد إنشاء نسخ من الملفات الموجودة في أحد المجلدات إلى الخادوم أو مشاركة قائمة بالمقطوعات الموسيقية التي تحبها مع أصدقائك، ففي هذه الحالات سترغب بمضاعفة مجموعات من البيانات (أو «ضربها»). لنضرب البنية coral بالرقم 2 والبنية kelp بالرقم 3، ثم نسندها إلى بنى tuple جديدة: multiplied_coral = coral * 2 multiplied_kelp = kelp * 3 print(multiplied_coral) print(multiplied_kelp) الناتج: ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral', 'blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral') ('wakame', 'alaria', 'deep-sea tangle', 'macrocystis', 'wakame', 'alaria', 'deep-sea tangle', 'macrocystis', 'wakame', 'alaria', 'deep-sea tangle', 'macrocystis') يمكننا باستخدام المعامل * أن نُكرِّر (أو نُضاعِف) بنى tuple بأي عدد من المرات نشاء، مما سينُشِئ بنى tuple جديدة اعتمادًا على محتوى البنى الأصلية. الخلاصة هي أنَّ بنى tuple يمكن إضافتها إلى بعضها أو ضربها لتشكيل بنى tuple جديدة عبر استخدام المعاملَين + و *. دوال التعامل مع tuple هنالك دوال مُضمَّنة في لغة بايثون للتعامل مع بنى tuple، لننظر إلى بعضها. len()‎ وكما في السلاسل النصية والقوائم، يمكننا حساب طول (أو عدد عناصر) بنية tuple باستخدام الدالة len()‎ حيث نُمرِّر إليها بنية tuple كمعامل (parameter)، كما يلي: len(coral) هذه الدالة مفيدة إذا أردنا أن نَضمَن أنَّ لبنية tuple عدد عناصر معيّن، فمثلًا يمكننا الاستفادة من ذلك بمقارنة بنيتين مع بعضهما. إذا أردنا طباعة عدد عناصر kelp و numbers، فسيظهر الناتج الآتي: print(len(kelp)) print(len(numbers)) الناتج: 4 13 الناتج أعلاه يشير إلى أنَّ للبنية kelp أربعة عناصر: kelp = ('wakame', 'alaria', 'deep-sea tangle', 'macrocystis') أما البنية numbers فتملك ثلاثة عشر عنصرًا: numbers = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) وصحيحٌ أنَّ هذه الأمثلة عناصرها قليلة نسبيًا، إلا أنَّ الدالة len()‎ تستطيع أن تخبرنا بعدد عناصر بنى tuple الكبيرة. الدالتان max()‎ و min()‎ عندما نتعامل مع بنى tuple مكوَّنة من عناصر رقمية (بما فيها الأعداد الصحيحة والأرقام ذات الفاصلة العشرية)، فيمكننا استخدام الدالتين max()‎ و min()‎ للعثور على أكبر وأصغر قيمة موجودة في بنية tuple معيّنة. تسمح لنا هاتان الدالتان باستخراج معلومات تخص البيانات القابلة للإحصاء، مثل نتائج الامتحانات أو درجات الحرارة أو أسعار المنتجات …إلخ. لننظر إلى بنية tuple مكونة من أعداد عشرية: more_numbers = (11.13, 34.87, 95.59, 82.49, 42.73, 11.12, 95.57) للحصول على القيمة العظمى من بين القيم الآتية فعلينا تمرير بنية tuple إلى الدالة max()‎ كما في max(more_numbers)، وسنستخدم الدالة print()‎ لعرض الناتج: print(max(more_numbers)) 95.59 أعادت الدالة max()‎ أعلى قيمة في بنية more_numbers. وبشكلٍ شبيهٍ بما سبق نستخدم الدالة min()‎: print(min(more_numbers)) 11.12 أُعيدَ هنا أصغر رقم عشري موجودة في البنية. يمكن الاستفادة من الدالتين max()‎ و min()‎ كثيرًا للتعامل مع بنى tuple التي تحتوي الكثير من القيم. كيف تختلف بنى tuple عن القوائم (list) الفرق الرئيسي بين tuple و list هو عدم القدرة على تعديلها، وهذا يعني أنَّنا لا نستطيع إضافة أو حذف أو استبدال العناصر داخل بنية tuple. لكن يمكننا إضافة بنيتَي tuple أو أكثر إلى بعضها بعضًا لتشكيل بنية جديدة كما رأينا في أحد الأقسام السابقة. لتكن لدينا البنية coral الآتية: coral = ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral') لنقل أننا نريد استبدال العنصر 'blue coral' ووضع العنصر 'black coral' بدلًا منه. فلو حاولنا تغيير بنية tuple بنفس الطريقة التي نُعدِّل فيها القوائم بكتابة: coral[0] = 'black coral' فستظهر رسالة خطأ كالآتية: TypeError: 'tuple' object does not support item assignment وذلك بسبب عدم إمكانية تعديل بنى tuple. إذا أنشأنا بنية tuple ثم قررنا أنَّ ما نحتاج له هو بنية list، فيمكننا تحويلها إلى قائمة list، وذلك بالدالة list()‎: list(coral) أصبحت بنية coral قائمةً الآن: coral = ['blue coral', 'staghorn coral', 'pillar coral'] يمكننا أن نلاحظ أنَّ بنية tuple تحوَّلتَ إلى قائمة list لأنَّ الأقواس المحيطة بالقيم أصبح مربعة الشكل. وبشكلٍ شبيهٍ بما سبق، نستطيع تحويل القوائم من النوع list إلى tuple باستخدام الدالة tuple()‎. الخلاصة نوع البيانات tuple هو مجموعةٌ من البيانات المتسلسلة التي لا يمكن تعديلها، ويوفِّر تحسينًا في أداء برامجك لأنه أسرع معالجةً من القوائم في بايثون. وعندما يراجع الآخرون شيفرتك فسيعلمون من استخدامك لبنى tuple أنك لا تريد تعديل هذه القيم. شرحنا في هذا الدرس الميزات الأساسية لبنى tuple بما في ذلك الفهارس وتقطيعها وتجميعها، وعرضنا بعض الدوال المُضمَّنة المتوافرة لهذا النوع من البيانات. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة –وبتصرّف– للمقال Understanding Tuples in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة التالية: فهم القواميس في بايثون 3 المقالة السابقة: فهم كيفية استعمال List Comprehensions في بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون دليلك الشامل إلى أنواع البيانات
  4. تُستخدَم أنواع البيانات في بايثون لتصنيف نوع معيَّن من البيانات، وتحديد القيم التي يمكنك تعيينها لذلك النوع، والعمليات التي يمكنك إجراؤها عليها. هناك أوقات نحتاج إلى تحويل القيم من نوع إلى آخر لأجل معالجتها بطريقة مختلفة. على سبيل المثال، قد نحتاج إلى ضم (concatenate) القيم العددية إلى سلاسل نصية. سترشدك هذه المقالة إلى كيفية تحويل الأعداد والسلاسل النصية والصفوف والقوائم، بالإضافة إلى تقديم بعض الأمثلة التوضيحية. تحويل الأنواع العددية هناك نوعان من البيانات العددية في بايثون: الأعداد الصحيحة والأعداد العشرية. ستعمل في بعض الأحيان على شيفرة برمجية كتبها شخص آخر، وقد تحتاج إلى تحويل عدد صحيح إلى عدد عشري، أو العكس، أو قد تجد أنك تستخدم عددًا صحيحًا في الوقت الذي تحتاج إلى أعداد عشرية. يتوفر في بايثون توابع مضمّنة تُسهِّل عليك تحويل الأعداد الصحيحة إلى أعداد عشرية، أو العكس. تحويل الأعداد الصحيحة إلى أعداد عشرية يحوّل التابع float()‎ الأعداد الصحيحة إلى أعداد عشرية. لاستخدام هذه الدالة، ضع عددًا صحيحًا بين القوسين: float(57) في هذه الحالة، سيتم تحويل 57 إلى 57.0. يمكنك أيضًا استخدام هذه الدالة مع المتغيرات. لنُحِل على المتغير f القيمة 57، ثم نطبع العدد العشري الجديد: f = 57 print(float(f)) الناتج سيكون: 57.0 يمكننا باستخدام الدالة float()‎ تحويل الأعداد الصحيحة إلى أعداد عشرية. دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن تحويل الأعداد العشرية إلى أعداد صحيحة بايثون لديه دالة أخرى مضمَّنة لتحويل الأعداد عشرية إلى أعداد صحيحة: وهي int()‎. تعمل الدالة int()‎ بشكل مشابه للدالة float()‎: يمكنك إضافة عدد عشري داخل القوسين لتحويله إلى عدد صحيح: int(390.8) في هذه الحالة، سيتم تحويل 390.8 إلى 390. يمكنك أيضا استخدام هذه الدالة مع المتغيرات. لنصرِّح أنَّ b يساوي 125.0، وأنَّ c يساوي 390.8، ثم نطبع العددين العشريين الجديدين: b = 125.0 c = 390.8 print(int(b)) print(int(c)) والمخرجات ستكون: 125 390 عند تحويل الأعداد العشرية إلى أعداد صحيحة بواسطة الدالة int()‎، فإنّ بايثون تقتطع الأجزاء العشرية من العدد وتُبقي القيمة الصحيحة؛ لذلك، لن تُحوِّل الدالة int()‎ العدد 390.8 إلى 391. تحويل الأعداد عبر القسمة في بايثون 3، عند تقسيم عدد صحيح على آخر، سينتج عدد عشرية على خلاف بايثون 2. بمعنى أنه عند قسمة 5 على 2 في بايثون 3، ستحصل على عدد عشري (مثل 2.5 عند قسمة 5 على 2): a = 5 / 2 print(a) وسينتج لنا: 2.5 في بايثون 2، ستحصل على ناتج صحيح، أي 5/2 = 2. يمكنك الحصول على عدد صحيح ناتج عن عملية القسمة باستعمال المعامل // الجديد في بايثون 3: a = 5 // 2 print(a) وسينتج لنا: 2 اقرأ درس اعتبارات عملية للاختيار ما بين بايثون 2 و بايثون 3 إن لم تقرأه مسبقًا. التحويل مع السلاسل النصية السلاسل النصية عبارة عن سلسلة مؤلفة من محرف واحد أو أكثر (المحرف يمكن أن يكون حرفًا، أو عددًا، أو رمزًا). السلاسل النصية هي إحدى الأشكال الشائعة من البيانات في عالم البرمجة، وقد نحتاج إلى تحويل السلاسل النصية إلى أعداد أو أعداد إلى سلاسل نصية في كثير من الأحيان، خاصةً عندما نعمل على البيانات التي ينشئها المستخدمون. تحويل الأعداد إلى سلاسل نصية يمكننا تحويل الأعداد إلى سلاسل نصية عبر التابع str()‎. يمكننا أن نمرّر إمَّا عددًا أو متغيرًا بين قوسي التابع، وبعد ذلك سيتم تحويل تلك القيمة العددية إلى قيمة نصية. دعنا ننظر أولًا في تحويل الأعداد الصحيحة. لتحويل العدد الصحيح 12 إلى سلسلة نصية، يمكنك تمرير 12 إلى التابع str()‎: str(12) عند تنفيذ str(12)‎ في سطر أوامر بايثون التفاعلي مع الأمر python في نافذة الطرفية، ستحصل على المخرجات التالية: '12' تشير علامات الاقتباس المحيطة بالعدد 12 إلى أنه لم يعد عددًا صحيحًا، ولكنه أصبح الآن سلسلة نصية. سيصبح باستخدام المتغيرات تحويل الأعداد الصحيحة إلى سلاسل نصية أكثر فائدة. لنفترض أننا نريد متابعة تقدم مستخدم في مجال البرمجة يوميًّا مثل أن ندخل عدد أسطر الشيفرة البرمجية التي كتبها. نود أن نعرض ذلك على المستخدم، وذلك بطباعة السلاسل النصية والأعداد في الوقت نفسه: user = "Sammy" lines = 50 print("Congratulations, " + user + "! You just wrote " + lines + " lines of code.") عند تنفيذ الشيفرة أعلاه، سيُطلق الخطأ التالي: TypeError: Can't convert 'int' object to str implicitly يتعذر علينا ضمّ (concatenate) السلاسل النصية والأعداد الصحيحة في بايثون، لذلك سيتعين علينا تحويل المتغير lines إلى سلسلة نصية: user = "Sammy" lines = 50 print("Congratulations, " + user + "! You just wrote " + str(lines) + " lines of code.") الآن، عندما نُنفِّذ الشيفرة البرمجية، سنحصل على المخرجات التالية، وفيها تهنئة للمستخدم على تقدُّمه: Congratulations, Sammy! You just wrote 50 lines of code. إذا أردنا تحويل عدد عشري إلى سلسلة نصية بدلًا من تحويل عدد صحيح إلى سلسلة نصية، فعلينا تتبع نفس الخطوات والصياغة السابقة. عندما نمرّر عددًا عشريًا إلى التابع str()‎، ستُعاد سلسلة نصية. يمكننا استخدام قيمة العدد العشري نفسها، أو يمكننا استخدام متغير: print(str(421.034)) f = 5524.53 print(str(f)) وسينتج لنا: 421.034 5524.53 يمكننا اختبار صحة التحويل عن طريق ضم الناتج إلى سلسلة نصية: f = 5524.53 print("Sammy has " + str(f) + " points.") وهذا هو الناتج: Sammy has 5524.53 points. الآن تأكدنا من أنَّ عددنا العشري قد حُوِّل بنجاح إلى سلسلة نصية، لأنّ عملية الضم قد تم إجراؤها دون خطأ. تحويل السلاسل النصية إلى أعداد يمكن تحويل السلاسل النصية إلى أعداد باستخدام التابعين float()‎ و int()‎. إذا لم يكن في السلسلة النصية منازل عشرية، فالأفضل أن تحولها إلى عدد صحيح باستخدام التابع int()‎. دعنا نستخدم مثال تتبع عدد أسطر الشيفرة الذي أوردناه أعلاه. قد ترغب في التعامل مع هذه القيم باستخدام الحسابات الرياضياتية لتقديم نتائج أدق للمستخدم، ولكنّ هذه القيم مخزّنة حاليًا في سلاسل نصية: lines_yesterday = "50" lines_today = "108" lines_more = lines_today - lines_yesterday print(lines_more) الناتج هو: TypeError: unsupported operand type(s) for -: 'str' and 'str' نظرًا لأنّ القيمتين العدديتين مخزنتان في سلاسل نصية، تلقينا خطأً. سبب ذلك أنَّ معامل الطرح - لا يصلح للسلاسل النصية. دعنا نعدّل الشيفرة لتضمين التابع int()‎ الذي سيحول السلاسل النصية إلى أعداد صحيحة، ويسمح لنا بالقيام بالعمليات الرياضياتية على القيم التي كانت سلاسل نصية في الأصل. lines_yesterday = "50" lines_today = "108" lines_more = int(lines_today) - int(lines_yesterday) print(lines_more) وهذه هي المخرجات: 58 المتغير line_more هو عدد صحيح تلقائيًّا، ويساوي القيمة العددية 58 في هذا المثال. يمكننا أيضًا تحويل الأعداد في المثال أعلاه إلى قيم عشرية باستخدام التابع float()‎ بدلًا من التابع int()‎. وبدلًا من الحصول على الناتج 58، سنحصل على الناتج 58.0، وهو عدد عشري. المستخدم Sammy سيكسب نقاطًا على شكل قيم عشرية: total_points = "5524.53" new_points = "45.30" new_total_points = total_points + new_points print(new_total_points) الناتج: 5524.5345.30 في هذه الحالة، يعد استخدام المعامل + مع سلسلتين نصيتين عمليةً صالحةً، لكنه سيضم السلسلتين النصّيتين بدلًا من جمع القيمتين العدديتين؛ لذلك، سيبدو الناتج غير اعتيادي، لأنه نتيجة لصق القيمتين إلى جانب بعضهما بعضًا. سنحتاج إلى تحويل هذه السلاسل النصية إلى أعداد عشرية قبل إجراء أي عمليات عليها، وذلك باستخدام التابع float()‎: total_points = "5524.53" new_points = "45.30" new_total_points = float(total_points) + float(new_points) print(new_total_points) وسينتج عن ذلك: 5569.83 الآن، وبعد أن حوّلنا السلسلتين النصيتين إلى عددين عشريين، سنحصل على النتيجة المتوقعة، والتي هي جمع 45.30 و 5524.53. إذا حاولنا تحويل سلسلة نصية ذات منازل عشرية إلى عدد صحيح، فسنحصل على خطأ: f = "54.23" print(int(f)) المخرجات: ValueError: invalid literal for int() with base 10: '54.23' إذا مرّرنا عددًا عشريًا موضوعًا في سلسلة نصية إلى التابع int()‎، فسنحصل على خطأ، لأنها لن تُحوَّل إلى عدد صحيح. يتيح لنا تحويل السلاسل النصية إلى أعداد تعديل نوع البيانات الذي نعمل عليه بسرعة حتى نتمكن من إجراء عمليات على قيم عددية مكتوبة على شكل سلاسل نصية. التحويل إلى صفوف وقوائم يمكنك استخدام التابعين list()‎ و tuple()‎ لتحويل القيم المُمرّرة إليهما إلى قائمة أو صف على التوالي. في بايثون: القائمة هي تسلسل مرتب قابل للتغيير من العناصر الموضوعة داخل قوسين معقوفين []. الصف عبارة عن تسلسل مرتب ثابت (غير قابل للتغيير) من العناصر الموضوعة بين قوسين (). التحويل إلى صفوف نظرًا لكون الصفوف غير قابلة للتغيير، فيمكن أن يحسِّن تحويل قائمة إلى صف أداء البرامج تحسينًا كبيرًا. عندما نستخدم التابع tuple()‎، فسوف يُعيد القيمة المُمرَّرة إليه على هيئة صف. print(tuple(['pull request', 'open source', 'repository', 'branch'])) المخرجات: ('pull request', 'open source', 'repository', 'branch') نرى أنّ الصف قد طُبع في المخرجات، إذ أنَّ العناصر موضوعة الآن بين قوسين، بدلًا من القوسين المربعين. دعنا نستخدم tuple()‎‎ مع متغير يحتوي قائمة: sea_creatures = ['shark', 'cuttlefish', 'squid', 'mantis shrimp'] print(tuple(sea_creatures)) سينتج: ('shark', 'cuttlefish', 'squid', 'mantis shrimp') مرة أخرى، نرى أنّ القائمة حُوِّلت إلى صف، كما يشير إلى ذلك القوسان. يمكننا تحويل أي نوع قابل للتكرار (iterable) إلى صف، بما في ذلك السلاسل النصية: print(tuple('Sammy')) المخرجات: ('S', 'a', 'm', 'm', 'y') نظرًا لأنه يمكننا التكرار (iterate) على السلاسل النصية، يمكننا تحويلها إلى صفوف باستخدام التابع tuple()‎. أمَّا أنواع البيانات غير القابلة للتكرار، مثل الأعداد الصحيحة والأعداد العشرية، فستُطلق عملية تحويلها خطأً: print(tuple(5000)) والناتج سيكون: TypeError: 'int' object is not iterable في حين أنه من الممكن تحويل عدد صحيح إلى سلسلة نصية، ومن ثم تحويل السلسلة النصية إلى صف، كما في العبارة tuple(str(5000))‎، فمن الأفضل تجنب مثل هذه التعليمات البرمجية المعقدة. التحويل إلى قوائم يمكن أن يكون تحويل القيم، وخاصة الصفوف، إلى قوائم مفيدًا عندما تحتاج إلى نسخة قابلة للتغيير من تلك القيم. سنستخدم التابع list()‎ لتحويل الصف التالي إلى قائمة. ونظرًا لأنّ صياغة القوائم تستخدم الأقواس، تأكد من تضمين أقواس التابع list()‎، وكذلك الأقواس الخاصة بالتابع print()‎: print(list(('blue coral', 'staghorn coral', 'pillar coral'))) المخرجات هي: ['blue coral', 'staghorn coral', 'pillar coral'] تشير الأقواس المعقوفة [] إلى أنَّه قد تم إرجاع قائمة من الصف الأصلي الذي مُرِّر عبر التابع list()‎. لجعل الشيفرة سهلة القراءة، يمكننا إزالة أحد أزواج الأقواس باستخدام متغير: coral = ('blue coral', 'staghorn coral', 'pillar coral') list(coral) إن طبعنا list(coral)‎، فسنتلقى المخرجات نفسها الموجودة أعلاه. تمامًا مثل الصفوف، يمكن تحويل السلاسل النصية إلى قوائم: print(list('shark')) الناتج: ['s', 'h', 'a', 'r', 'k'] هنا حُوِّلَت السلسلة shark إلى قائمة، وهذا يوفر لنا نسخة قابلة للتغيير من القيمة الأصلية. خلاصة لقد وضحنا في هذه المقالة كيفية تحويل العديد من أنواع البيانات الأصلية المهمة إلى أنواع بيانات أخرى، وذلك باستخدام التوابع المُضمّنة. تحويل أنواع البيانات في بايثون يوفر لك مرونةً إضافيةً في مشاريعك البرمجية. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة -وبتصرّف- للمقال How To Convert Data Types in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة التالية: كيفية استخدام المتغيرات المقالة السابقة: آلية فهرسة السلاسل النصية وطريقة تقسيمها المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  5. تُستخدم في بايثون، كما هو الحال في جميع لغات البرمجة، أنواع البيانات لتصنيف البيانات. هذا مهم لأنّ نوع البيانات الذي تستخدمه سيحدد القيم التي يمكن تعيينها لها، وما الذي يمكن فعله بها (بما في ذلك العمليات التي يمكن تنفيذها عليها). سنتعرف في هذه المقالة على أهم أنواع البيانات الأصلية لبايثون. هذا ليس استقصاءً شاملًا لأنواع البيانات، ولكنه سيساعدك على التعرف على الخيارات المتاحة لك في بايثون. مدخل أنواع البيانات في بايثون مشابهة إلى حد ما لأنواع البيانات التي نستخدمها في العالم الحقيقي. من أمثلة أنواع البيانات في العالم الحقيقي الأعداد، مثل: الأعداد الصحيحة الطبيعية (0، 1، 2، ...)، والأعداد الصحيحة النسبية (...، 1-، 0، 1، ...)، والأعداد غير النسبية (π). يمكننا عادة في الرياضيات جمع أعداد من أنواع مختلفة مثل إضافة 5 إلى π: 5 + π يمكننا إما الاحتفاظ بالمعادلة كإجابة، وستكون النتيجة عددًا غير نسبي (irrational number)، أو يمكننا تقريب (round) العدد π إلى عدد ذي منازل عشرية محددة، ثم نجمع العددين: 5 + π = 5 + 3.14 = 8.14 ولكن، إذا حاولنا إضافة عدد إلى نوع بيانات آخر، مثل الكلمات، فستصبح الأمور مربكة وغير ذات معنى. فكيف ستحل المعادلة التالية مثلًا؟ hsoub + 8 بالنسبة إلى الكلمة hsoub، يمكن عدكل نوع من أنواع البيانات مختلفًا تمامًا، مثل الكلمات والأعداد، لذلك يتعين علينا توخي الحذر بشأن كيفية استخدامها، وكيفية التعامل معها في العمليات. الأعداد سيُفسَّر كل عدد تُدخله إلى بايثون كعدد؛ ليس مطلوبًا منك إعلان نوع البيانات الذي تدخله لأنّ بايثون تَعدُّ أيّ عدد مكتوب بدون فواصل عشرية بمثابة عدد صحيح (integer، كما هو حال 138)، وأيّ عدد مكتوب بفواص لعشرية بمثابة عدد عشري (float كما هو حال 138.0). الأعداد الصحيحة كما هو الحال في الرياضيات، الأعداد الصحيحة (integer) في البرمجة هي أعداد كاملة، يمكن أن تكون موجبة أو سالبة أو معدومة (...، ‎1،0،-1، ...). ويُعرف هذا النوع أيضًا باسم int. كما هو الحال مع لغات البرمجة الأخرى، يجب ألا تستخدم الفواصل في الأعداد المؤلفة من أربعة أرقام أو أكثر، لذلك لا تكتب 1,000 في برنامجك، واكتب 1000. يمكننا طباعة العدد الصحيح على النحو التالي: print(-25) وسينتج: -25 أو يمكننا الإعلان عن متغير، والذي هو في هذه الحالة رمزٌ للعدد الذي نستخدمه أو نتعامل معه، مثلًا: my_int = -25 print(my_int) وسينتج لنا: -25 يمكننا أن نجري العمليات الحسابية على الأعداد الصحيحة في بايثون: int_ans = 116 - 68 print(int_ans) المخرجات: 48 يمكن استخدام الأعداد الصحيحة بعدة طرق في برامج بايثون، ومع استمرارك في تعلم المزيد عن هذه اللغة، ستتاح لك الكثير من الفرص لاستخدام الأعداد الصحيحة وفهم المزيد عن هذا النوع من البيانات. دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن الأعداد العشرية الأعداد العشرية (Floating-Point Numbers) هي أعداد حقيقية، مما يعني أنه يمكن أن تكون أعدادًا جذرية أو غير نسبية. لهذا السبب، يمكن أن تحتوي الأعداد العشرية على جزء كسري، مثل 9.0 أو ‎-116.42. وببساطة، فالأعداد العشرية هي أعداد تحتوي الفاصلة العشرية. كما فعلنا مع الأعداد الصحيحة، يمكننا طباعة الأعداد العشرية هكذا: print(17.3) وسينتج لنا: 17.3 يمكننا أيضًا أن نعلن عن متغير يحتوى عددًا عشريًا، مثلًا: my_flt = 17.3 print(my_flt) الناتج: 17.3 وكما هو الحال مع الأعداد الصحيحة، يمكننا أن نجرى العمليات الحسابية على الأعداد العشرية: flt_ans = 564.0 + 365.24 print(flt_ans) الناتج: 929.24 الأعداد الصحيحة والأعداد العشرية مختلفان عمومًا، إذ أنّ 3 ≠ 3.0، لأنّ 3 عدد صحيح، بينما 3.0 عدد عشري. القيم المنطقية هناك قيمتان فقط لنوع البيانات المنطقية (Boolean) وهما True و False. تُستخدم القيم المنطقية لتمثيل قيم الحقيقة الموافقة للمنطق الرياضياتي. عادة ما يبدأ اسم البيانات المنطقية بالحرف B، إشارة إلى اسم عالم الرياضيات George Boole. القيمتان True و False تُكتبان دائمًا بحرفين كبيرين T و F، لأنها قيم خاصة في بايثون. الكثير من العمليات الحسابية في الرياضيات تُنتج قيمًا منطقيًا، إما True أو False: أكبر من 500 > 100 True 1 > 5 False أصغر من 200 < 400 True 4 < 2 False التساوي 5 = 5 True 500 = 400 False كما هو الحال مع الأعداد، يمكننا تخزين القيم المنطقية في المتغيرات: my_bool = 5 > 8 يمكننا بعد ذلك طباعة القيمة المنطقية باستدعاء الدالة print()‎: print(my_bool) بما أنّ العدد 5 ليس أكبر من 8، فسوف نحصل على المخرجات التالية: False ستتعلم مع مرور الوقت كيفية استخدام القيم المنطقية، وكيف يمكن للدوال والعمليات المنطقية أن تغير مسار البرنامج. السلاسل النصية السلسلة النصية (string) هي عبارة عن تسلسل من محرف واحد أو أكثر (محارف وأعداد ورموز)، ويمكن أن تكون ثابتة أو متغيرة. تحاط السلاسل النصية إما بعلامات الاقتباس المفردة ' أو علامات الاقتباس المزدوجة "، لذلك لإنشاء سلسلة نصية، ضع سلسلة من الأحرف بين علامتي اقتباس: 'هذه سلسلة نصية ضمن علامتي اقتباس مفردتين' "هذه سلسلة نصية ضمن علامتي اقتباس مزدوجتين" يمكنك استخدام علامات الاقتباس المفردة أو علامات الاقتباس المزدوجة، المهم أن تكون متسقًا في برنامجك. البرنامج البسيط "Hello, World!‎" يوضح كيف يمكن استخدام السلاسل النصية في البرمجة، حيث أنّ حروف عبارة Hello، World!‎ تمثل سلسلة نصية. print("Hello, World!") كما هو الحال مع أنواع البيانات الأخرى، يمكننا تخزين السلاسل النصية في المتغيرات: hw = "Hello, World!" وطباعة السلسلة عن طريق استدعاء المتغير: print(hw) // Hello, World! مثل الأعداد، هناك العديد من العمليات التي يمكن إجراؤها على السلاسل النصية من أجل تحقيق النتائج التي نسعى إليها. السلاسل النصية مهمة لتوصيل المعلومات إلى المستخدم، وكذلك لتمكين المستخدم من تمرير المعلومات إلى البرنامج. القوائم القائمة (lists) عبارة عن تسلسل مرتّب قابل للتغيير (mutable). وكما تُعرّف السلاسل النصية باستخدام علامات الاقتباس، يتم تعريف القوائم باستخدام الأقواس المعقوفة []. مثلًا، هذه قائمة تحوي أعدادًا صحيحةً: [-3, -2, -1, 0, 1, 2, 3] وهذه قائمة من الأعداد العشرية: [3.14, 9.23, 111.11, 312.12, 1.05] وهذه قائمة من السلاسل النصية: ['shark', 'cuttlefish', 'squid', 'mantis shrimp'] في المثال التالي، سنسمّى قائمة السلاسل النصية خاصتنا sea_creatures: sea_creatures = ['shark', 'cuttlefish', 'squid', 'mantis shrimp'] يمكننا طباعتها عن طريق استدعاء المتغير: print(sea_creatures) وسترى أنّ المخرجات تشبه تمامًا القائمة التي أنشأناها: ['shark', 'cuttlefish', 'squid', 'mantis shrimp'] القوائم هي نوع بيانات مرن للغاية، لأنها قابلة للتغيير، حيث يمكن إضافة قيم إليها، أو إزالته، أو تغييرها. هناك نوع بيانات آخر مشابه لقوائم، بيْد أنه غير قابل للتغيير، ويُسمى الصف (tuple). الصفوف (Tuples) يُستخدم الصف (tuple) لتجميع البيانات. إنه تسلسل ثابت من العناصر وغير قابل للتغيير. الصفوف تشبه القوائم إلى حد كبير، لكنها تستخدم الأقواس () بدلًا من الأقواس المعقوفة []، ولأنها غير قابلة للتغيير، فلا يمكن تغيير أو تعديل قيمها. تبدو الصفوف كالتالي: ('blue coral', 'staghorn coral', 'pillar coral') يمكننا تخزين الصفوف في المتغيرات وطباعتها: coral = ('blue coral', 'staghorn coral', 'pillar coral') print(coral) والمخرجات هي: ('blue coral', 'staghorn coral', 'pillar coral') كما هو الحال في أنواع البيانات الأخرى، تطبع بايثون الصفوف تمامًا كما كتبناها، حيث تطبع سلسلة من القيم بين قوسين. القواميس القاموس (Dictionaries) هو نوع مُضمّن في بايثون، إذ تُربط مفاتيحُ بالقيم المقابلة لها في شكل أزواج، هذه الأزواج مفيدة لتخزين البيانات في بايثون. يتم إنشاء القواميس باستخدام الأقواس المعقوصة {}. تُستخدم القواميس عادةً لحفظ البيانات المترابطة، مثل المعلومات المقابلة لرقم تعريف. يبدو القاموس كما يلي: {'name': 'Sammy', 'animal': 'shark', 'color': 'blue', 'location': 'ocean'} ستلاحظ أنه بالإضافة إلى الأقواس المعقوصة، توجد علامات النقطتين الرأسيتين (colons) داخل القاموس. الكلمات الموجودة على يسار النقطتين الرأسيتين هي المفاتيح. المفاتيح قد تكون أيّ نوع بيانات غير قابل للتغيير. المفاتيح في القاموس أعلاه هي: 'name', 'animal', 'color', 'location'‎. الكلمات الموجودة على يمين النقطتين هي القيم. يمكن أن تتألف القيم من أي نوع من البيانات. القيم في القاموس أعلاه هي: 'Sammy', 'shark', 'blue', 'ocean'‎. مثل أنواع البيانات الأخرى، يمكننا تخزين القواميس في متغيرات، وطباعتها: sammy = {'name': 'Sammy', 'animal': 'shark', 'color': 'blue', 'location': 'ocean'} print(sammy) والمخرجات هي: {'color': 'blue', 'animal': 'shark', 'name': 'Sammy', 'location': 'ocean'} إذا أردت الحصول على اللون (color) الخاص بـ Sammy، فيمكنك القيام بذلك عن طريق استدعاء sammy ['color']‎. هذا مثال على ذلك: print(sammy['color']) # blue القواميس من أنواع البيانات المهمة في برامج بايثون. خلاصة في هذه المرحلة، يُفترض أن يكون لديك فهم جيد لبعض أنواع البيانات الرئيسية المتاحة في بايثون. أنواع البيانات هذه ستصبح جزءًا طبيعيًا من حياتك كمبرمج للغة بايثون. يمكنك التعرف على المزيد من التفاصيل عن هذه الأنواع في موسوعة حسوب: الأعداد الصحيحة الأعداد العشرية القيم المنطقية السلاسل النصية القوائم الصفوف القواميس بمجرد أن تقوّي وتعمّق فهمك لأنواع البيانات المتاحة في بايثون، يمكنك الانتقال إلى تعلم كيفية تحويل أنواع البيانات في بايثون 3. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة -وبتصرّف- للمقال Understanding Data Types in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة التالية: مدخل إلى التعامل مع السلاسل النصية في بايثون 3 المقالة السابقة: كيفية كتابة التعليقات في بايثون 3 تعرف على أبرز مميزات لغة بايثون المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  6. Python هي لغةٌ سهلة القراءة للغاية ومتنوعة ومتعددة الاستخدامات، واسمها مستوحى من مجموعة كوميدية بريطانية باسم «Monty Python»، وكان أحد الأهداف الأساسية لفريق تطوير بايثون هو جعل اللغة مرحةً وسهلة الاستخدام، وإعدادها بسيطٌ، وطريقة كتابتها مباشرة وتعطيك تقريرًا مباشرًا عند حدوث أخطاء، وهي خيارٌ ممتازٌ للمبتدئين والوافدين الجدد على البرمجة. لغة بايثون هي لغة متعددة الاستعمالات، وتدعم مختلف أنماط البرمجة مثل كتابة السكربتات والبرمجة كائنية التوجه (object-oriented)، وهي مناسبةٌ للأغراض العامة، واستعمالها يتزايد في سوق العمل إذ تعتمدها منظماتٌ مثل «United Space Alliance» (شركة في مجال إرسال مركبات فضائية وتتعاقد معها ناسا) و «Industrial Light & Magic» (أستوديو للتأثيرات السينمائية وللرسوم المتحركة)، وتوفِّر بايثون قدراتٍ كثيرةٍ لمن يريد تعلم لغة برمجة جديدة. طوِّرَت اللغة في نهاية الثمانينات من القرن الماضي، ونُشِرَت أوّل مرة في عام 1991، طُوِّرَت بايثون من قِبل Guido van Rossum، وهو عضوٌ نشطٌ للغاية في المجتمع. وتعتبر بايثون على أنَّها بديلٌ عن لغة ABC، وأوّل إصدار منها كان يتضمن التعامل مع الاستثناءات (exception handling) والدوال والأصناف (classes) مع إمكانية الوراثة فيها. وعدما أُنشِئ منتدى محادثة في Usenet باسم comp.lang.python في 1994، فبدأت قاعدة مستخدمي بايثون بالنمو، مما مهّد الطريق لها لتصبح واحدة من أكثر لغات البرمجة شيوعًا وخصوصًا لتطوير البرمجيات مفتوحة المصدر. لمحة عامة قبل أن ننظر إلى إمكانيات إصدارَي بايثون 2 وبايثون 3 (مع الاختلافات البرمجية الرئيسية بينهما)، فلننظر إلى لمحة تاريخية عن الإصدارات الرئيسية الحديثة من بايثون. بايثون 2 نُشِرَ هذا الإصدار في أواخر عام 2000، وأصبحت بايثون 2 لغة برمجة شاملة مقارنةً بالإصدارات التي تسبقها وذلك بعد تطبيق اقتراح PEP ‏(Python Enhancement Proposal)، وهو مواصفةٌ (specification) تقنيةٌ التي توفِّر معلومات إلى أعضاء مجتمع بايثون أو تصف ميزاتٍ جديدة في اللغة. بالإضافة إلى ذلك، تضمنت بايثون 2 ميزاتٍ برمجية جديدة مثل «cycle-detecting garbage collector» لأتمتة عملية إدارة الذاكرة، وزيادة دعم يونيكود لتدعم اللغة جميع المحارف المعيارية …إلخ. وأثناء عملية تطوير بايثون 2 أضيفت ميزات جديدة بما في ذلك توحيد الأنواع والأصناف في بايثون في بنية هيكلية وحيدة (وذلك في إصدار 2.2 من بايثون). بايثون 3 تُعتَبر بايثون 3 هي مستقبل لغة بايثون وهي النسخة قيد التطوير من اللغة، وهذا إصدارٌ رئيسيٌ نُشِر في أواخر عام 2008 لإصلاح بعض المشاكل الجوهرية في تصميم الإصدارات السابقة من اللغة، وكان التركيز أثناء تطوير بايثون 3 هو تحسين الشيفرات التي تبنى عليها اللغة وحذف التكرارات، مما يعني أنَّ هنالك طريقة وحيدة فقط لإنجاز مهمّة معيّنة. التعديلات الأساسية التي حدثت في بايثون 3.0 تتضمن تغير العبارة print إلى دالة مُضمَّنة باللغة، وتحسين قسمة الأعداد الصحيحة، وتوفير دعم إضافي ليونيكود. في البداية، انتشرت بايثون 3 ببطء نتيجةً لعدم توافقيتها مع بايثون 2، مما يعني أنَّ على المستخدمين اختيار ما هو الإصدار الذي عليهم استخدامه. بالإضافة إلى ذلك، كانت الكثير من المكتبات البرمجية متاحةً فقط لبايثون 2، لكن بعد تقرير فريق تطوير بايثون 3 أنَّه يجب أن التخلي عن دعم بايثون 2، فبدأت عملية تحويل المكتبات إلى بايثون 3. يمكننا معرفة زيادة الاعتماد على بايثون 3 من خلال عدد الحزم البرمجية التي تدعم بايثون 3، والتي هي (في وقت كتابة هذا المقال) 339 من أصل 360 من أشهر الحزم. بايثون 2.7 بعد إصدار بايثون 3.0 في 2008، أُصدِرَت نسخة بايثون 2.7 في تموز 2010 وهي آخر إصدار من سلسلة ‎2.x، الغرض من إصدار بايثون 2.7 هو جعل الطريق ممهدًا أمام مستخدمي بايثون ‎2.x لتحويل برامجهم إلى بايثون 3 بتوفير بعض التوافقية بينهما. وهذه التوافقية تضمنت دعم بعض الوحدات المُحسّنة في 2.7 مثل unittest لأتمتة الاختبارات، و argparse لتفسير خيارات سطر الأوامر، وبعض الفئات في collections. ولخصوصية بايثون 2.7 ولكونها جسرًا واصلًا بين الإصدارات القديمة من بايثون 2 وبين بايثون 3.0، فأصبحت خيارًا شائعًا بين المبرمجين بسبب توافقيتها مع الكثير من المكتبات. عندما نتحدث اليوم عن بايثون 2، فنحن نشير عادةً إلى إصدار بايثون 2.7 لأنَّه أكثر إصدار مستخدم؛ لكنه يُعتَبَر أنَّه إصدارٌ قديم، وسيتوقف تطويره (التطوير الحالي هو إصلاح العلل فقط) تمامًا في 2020. الاختلافات الأساسية بغض النظر أنَّ بايثون 2.7 وبايثون 3 تتشاركان في الكثير من الأشياء، لكن لا يجدر بك أن تظن أنَّهما متماثلتان ويمكن تبديل الشيفرات بينهما. وعلى الرغم من أنَّك تستطيع كتابة شيفرات جيدة وبرامج مفيدة في أيّ إصدار منهما، لكن من المهم أن تفهم أنَّ هنالك بعض الاختلافات في بنية الشيفرات وفي طريقة تفسيرها. سأعرض هنا بعض الأمثلة، لكن عليك أن تعلم أنَّك ستواجه المزيد من الاختلافات أثناء مسيرة تعلمك لبايثون. print في بايثون 2، تُعامَل print كتعبيرٍ برمجيٍ بدلًا من كونها دالة، وهذا كان يثير ارتباكًا لأنَّ الكثير من الأمور داخل بايثون تتطلب تمرير وسائط (arguments) بين قوسين، إذا فتحتَ مُفسِّر بايثون 2 لطباعة «Sammy the Shark is my favorite sea creature»، فستكتب تعبير print الآتي: print "Sammy the Shark is my favorite sea creature" أما في بايثون 3، فستُعامَل print()‎ كدالة، لذا لطباعة السلسلة النصية السابقة، فيمكننا استخدام شكل استدعاء الدوال التقليدي كما يلي: print("Sammy the Shark is my favorite sea creature") هذا التعديل جعل من البنية اللغوية في بايثون موحدةً وسهَّلَ من التبديل بين مختلف دوال الطباعة فيها. يجدر بالذكر أنَّ الدالة print()‎ متوافقة مع بايثون 2.7، لذا ستعمل شيفرات بايثون التي تستعمل print()‎ بشكلٍ صحيحٍ في أيّ الإصدارَين. قسمة الأعداد الصحيحة في بايثون 2، أيُّ عددٍ تكتبه دون فواصل عشرية سيُعامَل على أنَّه من النوع integer، تأتي الإشكالية عندما تحاول قسمة الأعداد الصحيحة على بعضها، فتتوقع في بعض الأحيان حصولك على عددٍ عشري (تسمى أيضًا بالأعداد ذات الفاصلة العائمة float) كما في التعبير الرياضي: 5 / 2 = 2.5 لكن الأعداد الصحيحة في بايثون 2 لن تتحول إلى أعداد عشرية عندما تتطلب العملية التي تُجرى عليها ذلك. عندما يكون العددان الموجودان على جانبَي معامل القسمة / عددين صحيحين، فإن بايثون 2 ستجري عملية القسم وستُنتِج عددًا عشريًا إلا أنها ستُعيد العدد الصحيح الأصغر أو المساوي للناتج، وهذا يعني أنَّه لو كتبتَ ‎5 / 2 فستُعيد بايثون 2.7 العدد الصحيح الأصغر أو المساوي للعدد 2.5، وهو في هذه الحالة 2: a = 5 / 2 print a 2 لإعادة عدد عشري، فيجب إضافة فواصل عشرية إلى الأرقام التي ستُجري عليها عملية القسمة كما في ‎5.0 / 2.0 لكي تحصل على النتيجة المنطقية 2.5. أما في بايثون 3، فقسمة الأعداد الصحيحة أصبحت كما نتوقع: a = 5 / 2 print a 2.5 يمكنك استخدام ‎5.0 / 2.0 لإعادة 2.5، لكن إن أردتَ تقريب ناتج القسمة فاستخدم المعامل // الموجود في بايثون 3، كالآتي: a = 5 // 2 print a 2 هذا التعديل في بايثون 3 جعل من قسمة الأعداد الصحيحة أمرًا سهلًا، لكن هذه الميزة غير متوافقة مع بايثون 2.7. دعم محارف يونيكود عندما تتعامل لغات البرمجة مع السلاسل النصية (strings، والتي هي سلسلةٌ من المحارف)، فهي تفعل ذلك بطرائق مختلفة لكي تتمكن الحواسيب من تحويل الأعداد إلى أحرف ورموز. تستعمل بايثون 2 محارف ASCII افتراضيًا، لذا عندما تكتب "Hello, Sammy!‎" فستتعامل بايثون 2 مع السلسلة النصية كمجموعة من محارف ASCII، والتي هي محدودةٌ لحوالي مئتَي محرف، أي أنَّ محارف ASCII هي طريقة غير عملية لترميز المحارف خصوصًا المحارف غير اللاتينية (كالعربية مثلًا). لاستخدام ترميز محارف يونيكود (Unicode) الذي يدعم أكثر من 128000 محرف تابع للكثير من اللغات والرموز، فعليك أن تكتب u"Hello, Sammy!‎"‎ حيث تُشير السابقة u إلى Unicode. تستعمل بايثون 3 محارف يونيكود (Unicode) افتراضيًا، مما يوفِّر عليك بعض الوقت أثناء التطوير، ويمكنك كتابة وعرض عدد أكبر بكثير من المحارف في برنامجك بسهولة. يدعم يونيكود الكثير من المحارف بما في ذلك الوجوه التعبيرية (emojis)، واستعمالها كترميز محارف افتراضي يعني أنَّ الأجهزة المحمولة ستكون مدعومةً في مشاريعك تلقائيًا. إذا كنت تحب أنَّ تكون شيفرات بايثون 3 التي تكتبها متوافقةً مع بايثون 2، فأبقِ على حرف u قبل السلاسل النصية. استمرار التطوير الفارق الرئيسي بين بايثون 3 وبايثون 2 ليس في البنية اللغوية وإنما في أنَّ إصدار بايثون 2.7 سيتوقف دعمه في 2020، وسيستمر تطوير بايثون 3 بميزاتٍ جديدة وإصلاحٍ لمزيدٍ من العلل. التطويرات الأخيرة في اللغة تتضمن تخصيصًا أبسط لإنشاء الأصناف، وطريقةً أوضح للتعامل مع المصفوفات… الاستمرار بتطوير بايثون 3 يعني أنَّ المطورين يمكن أن يعتمدوا على اللغة، وسيطمئنون أنَّ المشاكل التي قد تحدث فيها ستُحَل في فترةٍ قريبة، ويمكن أن تصبح البرامج أكثر كفاءة بإضافة المزيد من الميزات للغة. نقاط أخرى يجب أخذها بالحسبان عليك أن تضع النقاط الآتية بعين الاعتبار عندما تبدأ مشوارك كمبرمج بلغة بايثون، أو عندما تبدأ بتعلم لغة بايثون بعد تعلمك لغيرها، وهنا ننصحك بتعلمها أكاديميًا باحترافية، من خلال الانضمام إلى دورة تطوير تطبيقات باستخدام لغة بايثون المقدمة من أكاديمية حسوب. إذا كنتَ تأمل بتعلم اللغة دون أن تفكِّر بمشروعٍ معيّن، فأنصحك بالتفكير بمستقبل بايثون، فسيستمر تطوير ودعم بايثون 3 بينما سيوقف دعم بايثون 2.7 عمّا قريب. أما إذا كنتَ تُخطِّط للانضمام لفريق تطوير أحد المشاريع، فعليك أن تنظر ما هو إصدار بايثون المستخدم فيه، وكيف يؤدي اختلاف الإصدار إلى اختلاف طريقة تعاملك مع الشيفرات، وإذا ما كانت المكتبات البرمجية المستعملة في المشروع مدعومةً في مختلف الإصدارات، وما هي تفاصيل المشروع نفسه… إذا كنت تُفكّر ببدء أحد المشاريع، فيجدر بك أن تنظر ما هي المكتبات المتوفرة وما هي إصدارات بايثون المدعومة. وكما قلنا سابقًا، الإصدارات الأوليّة من بايثون 3 لها توافقية أقل مع المكتبات المبنية لبايثون 2، لكن الكثير منها قد جرى تحويله إلى بايثون 3، وسيستمر ذلك في السنوات الأربع المقبلة. الخلاصة لغة بايثون كبيرة جدًا وموثقة توثيقًا ممتازًا وسهلة التعلم، ومهما كان اختيارك (بايثون 2 أو بايثون 3) فستتمكن من العمل على المشاريع الموجودة حاليًا. صحيحٌ أنّ هنالك بعض الاختلافات المحورية، لكن ليس من الصعب الانتقال من بايثون 3 إلى بايثون 2، وستجد عادةً أنَّ بايثون 2.7 قادرة على تشغيل شيفرات بايثون 3، خصوصًا في بدايات تعلمك للغة. من المهم أن تبقي ببالك أنَّ تركيز المطورين والمجتمع أصبح منصبًّا على بايثون 3، وسيصبح هذه اللغة رائدةً في المستقبل وستلبي الاحتياجات البرمجية المطلوبة، وأنَّ دعم بايثون 2.7 سيقل مع مرور الزمن إلى أن يزول في 2020. ترجمة -وبتصرّف- للمقال Python 2 vs Python 3: Practical Considerations لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة التالية: تثبيت بايثون على مختلف أنظمة التشغيل وإعداد بيئتها البرمجية المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون مميزات لغة بايثون
  7. تعدُّ المعاملات في تعاريف الدوال كيانات مسماة تُحدِّد وسيطًا (argument) يمكن أن يُمرَّر إلى الدالة المُعرَّفة. أثناء البرمجة، قد لا تدرك جميع حالات الاستخدام الممكنة للشيفرة، لذا قد ترغب في توسيع خيارات المبرمجين المستقبليين الذين سيستخدمون الوحدة التي طورتها. سنتعلم في هذا الدرس كيفيَّة تمرير عدد متغير من الوسائط إلى دالة ما باستخدام الصياغتين ‎*args‎ و ‎**kwargs‎. الوسيط ‎*args في بايثون، يمكن استخدام الشكل أحادي النجمة ‎*args‎ كمعامل لتمرير قائمة غير محددة الطول من الوسائط غير المسماة (non-keyworded argument) إلى الدوال. تجدر الإشارة إلى أنّ النجمة (‎*‎) عنصر ضروري هنا، إذ رغم أنّ الكلمة ‎args‎ متعارف عليها بين المبرمجين، إلا أنها غير رسمية. لنلقِ نظرة على مثال لدالة تستخدم وسيطين: def multiply(x, y): print (x * y) عرّفنا في الشفرة أعلاه دالة تقبل وسيطين ‎x‎ و ‎y‎، عندما نستدعي هذه الدالة، سنحتاج إلى تمرير عددين موافقين للوسيطين ‎x‎ و ‎y‎. في هذا المثال، سنمرّر العدد الصحيح ‎5‎ إلى ‎x‎، والعدد الصحيح ‎4‎ إلى ‎y‎: def multiply(x, y): print (x * y) multiply(5, 4) عند تنفيذ الشفرة أعلاه: python lets_multiply.py سنحصل على المخرجات التالية: 20 ماذا لو قررنا لاحقًا أننا نود حساب ناتج ضرب ثلاثة أعداد بدلًا من عددين فقط؟ إذا حاولت تمرير عدد إضافي إلى الدالة، كما هو موضح أدناه: def multiply(x, y): print (x * y) multiply(5, 4, 3) فسيُطلَق الخطأ التالي: TypeError: multiply() takes 2 positional arguments but 3 were given إذا شككت أنك ستحتاج إلى استخدام المزيد من الوسائط لاحقًا، فالحل هو استخدام ‎*args‎ كمعامل. سنزيل المُعاملين ‎x‎ و ‎y‎ من الشفرة في المثال الأول، ونضع مكانهما ‎*args‎: def multiply(*args): z = 1 for num in args: z *= num print(z) multiply(4, 5) multiply(10, 9) multiply(2, 3, 4) multiply(3, 5, 10, 6) عندما ننفّذ هذه الشفرة، سنحصل على ناتج استدعاءات الدالة أعلاه: 20 90 24 900 يمكننا باستخدام الوسيط ‎*args‎ تمرير أي عدد نحب من الوسائط عند استدعاء الدالة بالإضافة إلى كتابة شيفرة أكثر مرونة، وإنشاء دوال تقبل عددًا غير محدد مسبقًا من الوسائط غير المسماة. الوسيط ‎**kwargs يُستخدَم الشكل ذو النجمتين ‎**kwargs‎ لتمرير قاموس متغير الطول من الوسائط المسماة إلى الدالة المعرّفة. مرة أخرى، النجمتان (‎**‎) ضروريتان، فمع أنّ استخدام الكلمة ‎kwargs‎ متعارف عليه لدى المبرمجين، إلا أنها غير رسمية. يمكن أن تأخذ ‎**kwargs‎، كما هو شأن ‎*args‎، أيّ عدد من الوسائط التي ترغب في تمريرها إلى الدالة بيْد أنّ ‎**kwargs‎ تختلف عن ‎*args‎ في أنها تستوجب تعيين أسماء المعاملات (keywords). في المثال التالي، ستطبع الدالة الوسيط ‎**kwargs‎ الممرر إليها. def print_kwargs(**kwargs): print(kwargs) سنستدعي الآن الدالة ونمرر إليها بعض الوسائط المسماة: def print_kwargs(**kwargs): print(kwargs) print_kwargs(kwargs_1="Shark", kwargs_2=4.5, kwargs_3=True) لننفذ البرنامج أعلاه: python print_kwargs.py سنحصل على المخرجات التالية: {'kwargs_3': True, 'kwargs_2': 4.5, 'kwargs_1': 'Shark'} اعتمادًا على إصدار بايثون 3 الذي تستخدمه، فقد لا يكون القاموس مرتبًا. في بايثون 3.6 وما بعده، ستحصل على أزواج قيمة-مفتاح (key-value) مرتبة، ولكن في الإصدارات السابقة، سيكون ترتيب الأزواج عشوائيًا. سيُنشَأ قاموس يسمى ‎kwargs‎، والذي يمكننا التعامل معه مثل أي قاموس عادي داخل الدالة. لننشئ برنامجًا آخر لإظهار كيفية استخدام ‎**kwargs‎. سننشئ دالة تطبع قاموسًا من الأسماء. أولاً، سنبدأ بقاموس يحتوي اسمين: def print_values(**kwargs): for key, value in kwargs.items(): print("The value of {} is {}".format(key, value)) print_values(my_name="Sammy", your_name="Casey") بعد تنفيذ البرنامج: python print_values.py سنحصل على مايلي: The value of your_name is Casey The value of my_name is Sammy قد لا تكون القواميس مرتَّبة، لذلك قد يظهر الاسم ‎Casey‎ أولاً، وقد يظهر ثانيًا. سنمرر الآن وسائط إضافية إلى الدالة لنرى كيف يمكن أن تقبل ‎**kwargs‎ أيّ عدد من الوسائط: def print_values(**kwargs): for key, value in kwargs.items(): print("The value of {} is {}".format(key, value)) print_values( name_1="Alex", name_2="Gray", name_3="Harper", name_4="Phoenix", name_5="Remy", name_6="Val" ) إذا نفّذنا البرنامج الآن، فسنحصل على المخرجات التالية، والتي قد تكون غير مرتبة: The value of name_2 is Gray The value of name_6 is Val The value of name_4 is Phoenix The value of name_5 is Remy The value of name_3 is Harper The value of name_1 is Alex يتيح لك استخدام ‎**kwargs‎ مرونةً كبيرةً في استخدام الوسائط المسماة. فعند استخدامها، لن نحتاج إلى معرفة مسبقة بعدد الوسائط التي ستمرر إلى الدالة. ترتيب الوسائط عند الخلط بين عدة أنواع من الوسائط داخل دالة، أو داخل استدعاء دالة، يجب أن تظهر الوسائط وفق الترتيب التالي: الوسائط العادية ‎*args‎ الوسائط المسمّاة ‎**kwargs‎ عمليًا، عند الجمع بين المعاملات العادية، والوسيطين ‎*args‎ و ‎**kwargs‎، فينبغي أن تكون وفق الترتيب التالي: def example(arg_1, arg_2, *args, **kwargs): ... وعند الجمع بين المعاملات العادية والمعاملات المسماة و ‎*args‎ و ‎**kwargs‎، ينبغي أن تكون وفق الترتيب التالي: def example2(arg_1, arg_2, *args, kw_1="shark", kw_2="blobfish", **kwargs): ... من المهم أن تأخذ في الحسبان ترتيب الوسائط عند إنشاء الدوال حتى لا تتسبب في إطلاق خطأٍ متعلقٍ بالصياغة. استخدام ‎*args و ‎**kwargs في استدعاءات الدوال يمكننا أيضًا استخدام ‎*args‎ و ‎**kwargs‎ لتمرير الوسائط إلى الدوال. أولاً، دعنا ننظر إلى مثال يستخدم ‎*args‎: def some_args(arg_1, arg_2, arg_3): print("arg_1:", arg_1) print("arg_2:", arg_2) print("arg_3:", arg_3) args = ("Sammy", "Casey", "Alex") some_args(*args) في الدالة أعلاه، هناك ثلاثة معاملات، وهي ‎arg_1‎ و ‎arg_‎ و ‎arg_3‎. ستطبع الدالة كل هذه الوسائط. بعد ذلك أنشأنا متغيرًا، وأَحلنا عليه عنصرًا تكراريًا (في هذه الحالة، صف)، ثم مرَّرنا ذلك المتغير إلى الدالة باستخدام الصياغة النجمية (asterisk syntax). عندما ننفّذ البرنامج باستخدام الأمر ‎python some_args.py‎، سنحصل على المخرجات التالية: arg_1: Sammy arg_2: Casey arg_3: Alex يمكننا أيضًا تعديل البرنامج أعلاه، واستخدام قائمة. سندمج أيضًا ‎*args‎ مع وسيط مسمى: def some_args(arg_1, arg_2, arg_3): print("arg_1:", arg_1) print("arg_2:", arg_2) print("arg_3:", arg_3) my_list = [2, 3] some_args(1, *my_list) إذا نفذنا البرنامج أعلاه، فسنحصل على المخرجات التالية: arg_1: 1 arg_2: 2 arg_3: 3 وبالمثل، يمكن استخدام الوسائط المسماة ‎**kwargs‎ لاستدعاء دالة. سننشئ متغيرًا، ونسند إليه قاموسًا من 3 أزواج مفتاح-قيمة (سنستخدم ‎kwargs‎ هنا، ولكن يمكنك تسميته ما تشاء)، ثم نُمرِّره إلى دالة ذات 3 وسائط: def some_kwargs(kwarg_1, kwarg_2, kwarg_3): print("kwarg_1:", kwarg_1) print("kwarg_2:", kwarg_2) print("kwarg_3:", kwarg_3) kwargs = {"kwarg_1": "Val", "kwarg_2": "Harper", "kwarg_3": "Remy"} some_kwargs(**kwargs) عند تنفيذ البرنامج أعلاه باستخدام الأمر ‎python some_kwargs.py‎، سنحصل على المخرجات التالية: kwarg_1: Val kwarg_2: Harper kwarg_3: Remy خلاصة يمكنك استخدام الصياغتين الخاصتين ‎*args‎ و ‎**kwargs‎ ضمن تَعاريف الدوال لتمرير أيّ عدد تشاء من الوسائط إليها. يُستحسن استخدام ‎*args‎ و ‎**kwargs‎ في المواقف التي تتوقع أن يظل فيها عدد المدخلات في قائمة الوسائط صغيرًا نسبيًا. وضع في ذهنك أن استخدام ‎*args‎ و ‎**kwargs‎ يحسِّن مقروئية الشيفرة، ويسهِّل على المبرمجين، ولكن ينبغي استخدامهما بحذر. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة -وبتصرّف- للمقال How To Use *args and **kwargs in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة التالية: كيفية إنشاء الأصناف وتعريف الكائنات في بايثون 3 المقالة السابقة: كيفية تعريف الدوال في بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  8. القاموس هو نوع مُضمَّن في بايثون. تربط القواميس مفاتيح بقيم على هيئة أزواج، وهذه الأزواج مفيدة لتخزين البيانات في بايثون. تستخدم القواميس عادةً لتخزين البيانات المترابطة، مثل المعلومات المرتبطة برقم تعريف، أو ملفات تعريف المستخدم، وتُنشأ باستخدام الأقواس المعقوصة {}. تبدو القواميس على الشكل التالي: sammy = {'username': 'sammy-shark', 'online': True, 'followers': 987} بالإضافة إلى القوسين المعقوصين، لاحظ وجود النقطتين الرأسيتين (:) في القاموس. الكلمات الموجودة على يسار النقطتين الرأسيتين هي المفاتيح التي قد تكون أيَّ نوع بيانات غير قابل للتغيير. المفاتيح في القاموس أعلاه هي: username online *followers المفاتيح في المثال أعلاه عبارة عن سلاسل نصية. تمثِّل الكلمات الموجودة على يمين النقطتين «القيم». يمكن أن تتألف القيم من أي نوع من البيانات. القيم في القاموس أعلاه هي: sammy-shark True *987 قيم القاموس أعلاه هي إمَّا سلاسل نصية أو قيم منطقية أو أعداد صحيحة. سنطبع الآن القاموس sammy: print(sammy) # {'username': 'sammy-shark', 'followers': 987, 'online': True} نلاحظ بالنظر إلى المخرجات تغير ترتيب الأزواج قيمة-مفتاح (key-value). في بايثون 3.5 وما قبله، إذ القواميس غير مرتبة. لكن ابتداءً من بايثون 3.6، صارت القواميس مرتبةً. بغض النظر عما إذا كان القاموس مرتبًا أم لا، ستظل الأزواج قيمة-مفتاح كما هي، وهذا سيمكّنك من الوصول إلى البيانات بناء على ترابطاتها. الوصول إلى عناصر القاموس يمكننا الوصول إلى قيم محدَّدة في القاموس بالرجوع إلى المفاتيح المرتبطة بها ويمكن أيضًا الاستعانة ببعض التوابع الجاهزة للوصول إلى القيم أو المفاتيح أو كليهما. الوصول إلى عناصر القاموس باستخدام المفاتيح إذا أردنا الحصول على اسم المستخدم في Sammy، فيمكننا ذلك عن طريق استدعاء sammy['username']‎. هذا مثال على ذلك: print(sammy['username']) # sammy-shark تتصرف القواميس كقواعد البيانات، فهي بدلًا من فهرسة العناصر بأعداد صحيحة، كما هو الحال في القوائم، فإنها تُفهرس العناصر (أو قيم القاموس) بمفاتيح، ويمكنك عبر تلك المفاتيح الحصول على القيم المقابلة لها. باستدعاء المفتاح username، سنحصل على القيمة المرتبطة به، وهي sammy-shark. وبالمِثل، يمكن استدعاء القيم الأخرى في القاموس sammy باستخدام نفس الصياغة: sammy['followers'] # 987 sammy['online'] # True استخدام التوابع للوصول إلى العناصر بالإضافة إلى استخدام المفاتيح للوصول إلى القيم، يمكننا أيضًا استخدام بعض التوابع المُضمّنة، مثل: dict.keys()‎‎: الحصول على المفاتيح dict.values()‎: الحصول على القيم dict.items()‎: الحصول على العناصر على هيئة قائمة من أزواج (key, value) لإعادة المفاتيح، نستخدم التابع dict.keys()‎، كما يوضح المثال التالي: print(sammy.keys()) # dict_keys(['followers', 'username', 'online']) تلقينا في المخرجات كائنَ عرض تكراري (iterable view object) من الصنف dict_keys يحوي المفاتيح ثم طُبِعت المفاتيح على هيئة قائمة. يمكن استخدام هذا التابع للاستعلام من القواميس. على سبيل المثال، يمكننا البحث عن المفاتيح المشتركة بين قاموسين: sammy = {'username': 'sammy-shark', 'online': True, 'followers': 987} jesse = {'username': 'JOctopus', 'online': False, 'points': 723} for common_key in sammy.keys() & jesse.keys(): print(sammy[common_key], jesse[common_key]) يحوي القاموسان sammy و jesse معلومات تعريف المستخدم. كما أنّ لهما مفاتيح مختلفة، لأنّ لدى Sammy ملف تعريف اجتماعي يضم مفتاحًا followers يمثل المتابعين على الشبكة الاجتماعية، أما Jesse فلها ملف تعريف للألعاب يضم مفتاحًا points يمثل النقاط. كلا القاموسين يشتركان في المفتاحين username و online، ويمكن العثور عليهما عند تنفيذ هذا البُريمج: # المخرجات sammy-shark JOctopus True False يمكننا بالتأكيد تحسين البرنامج لتسهيل قراءة المخرجات، ولكنّ الغرض هنا هو توضيح إمكانية استخدام dict.keys()‎ لرصد المفاتيح المشتركة بين عدة قواميس. هذا مفيد بشكل خاص عند العمل على القواميس الكبيرة. وبالمثل، يمكننا استخدام التابع dict.values()‎ للاستعلام عن القيم الموجودة في القاموس sammy على النحو التالي: sammy = {'username': 'sammy-shark', 'online': True, 'followers': 987} print(sammy.values()) # dict_values([True, 'sammy-shark', 987]) يُعيد كلا التابعان values()‎ و keys()‎‎ قوائم غير مرتبة تضم مفاتيح وقيم القاموس sammy على هيئة كائِني عرضٍ من الصنف dict_values و dict_keys على التوالي. إن أردت الحصول على الأزواج الموجودة في القاموس، فاستخدم التابع items()‎: print(sammy.items()) المخرجات ستكون: dict_items([('online', True), ('username', 'sammy-shark'), ('followers', 987)]) النتيجة المعادة ستكون على هيئة قائمة مكونة من أزواج (key, value) من الصنف dict_items. يمكننا التكرار (iterate) على القائمة المُعادة باستخدام الحلقة for. على سبيل المثال، يمكننا طباعة جميع مفاتيح وقيم القاموس المحدد، ثم جعلها أكثر مقروئية عبر إضافة سلسلة نصية توضيحية: for key, value in sammy.items(): print(key, 'is the key for the value', value) وسينتج لنا: online is the key for the value True followers is the key for the value 987 username is the key for the value sammy-shark كرَّرت الحلقة for على العناصر الموجودة في القاموس sammy، وطبعت المفاتيح والقيم سطرًا سطرًا، مع إضافة معلومات توضيحية. تعديل القواميس القواميس هي هياكل بيانات قابلة للتغيير (mutable)، أي يمكن تعديلها. في هذا القسم، سنتعلم كيفية إضافة عناصر إلى قاموس، وكيفية حذفها. إضافة وتغيير عناصر القاموس يمكنك إضافة أزواج قيمة-مفتاح إلى قاموس دون استخدام توابع أو دوال باستخدام الصياغة التالية: dict[key] = value في المثال التالي، سنضيف زوجًا مفتاح-قيمة إلى قاموس يُسمى usernames: usernames = {'Sammy': 'sammy-shark', 'Jamie': 'mantisshrimp54'} usernames['Drew'] = 'squidly' print(usernames) # {'Drew': 'squidly', 'Sammy': 'sammy-shark', 'Jamie': 'mantisshrimp54'} لاحظ أنّ القاموس قد تم تحديثه بالزوج 'Drew': 'squidly'. نظرًا لأنّ القواميس غير مرتبة، فيمكن أن يظهر الزوج المُضاف في أيّ مكان في مخرجات القاموس. إذا استخدمنا القاموس usernames لاحقًا، فسيظهر فيه الزوج المضاف حديثًا. يمكن استخدام هذه الصياغة لتعديل القيمة المرتبطة بمفتاح معيّن. في هذه الحالة، سنشير إلى مفتاح موجود سلفًا، ونمرر قيمة مختلفة إليه. سنعرِّف في المثال التالي قاموسًا باسم drew يمثِّل البيانات الخاصة بأحد المستخدمين على بعض الشبكات الاجتماعية. حصل هذا المستخدم على عدد من المتابعين الإضافيين اليوم، لذلك سنحدّث القيمة المرتبطة بالمفتاح followers ثم نستخدم التابع print()‎ للتحقق من أنّ القاموس قد عُدِّل. drew = {'username': 'squidly', 'online': True, 'followers': 305} drew['followers'] = 342 print(drew) # {'username': 'squidly', 'followers': 342, 'online': True} في المخرجات نرى أنّ عدد المتابعين قد قفز من 305 إلى 342. يمكننا استخدام هذه الطريقة لإضافة أزواج قيمة-مفتاح إلى القواميس عبر مدخلات المستخدم. سنكتب بريمجًا سريعًا، usernames.py، يعمل من سطر الأوامر ويسمح للمستخدم بإضافة الأسماء وأسماء المستخدمين المرتبطة بها: # تعريف القاموس الأصلي usernames = {'Sammy': 'sammy-shark', 'Jamie': 'mantisshrimp54'} # while إعداد الحلقة التكرارية while True: # اطلب من المستخدم إدخال اسم print('Enter a name:') # name تعيين المدخلات إلى المتغير name = input() # تحقق مما إذا كان الاسم موجودًا في القاموس ثم اطبع الرد if name in usernames: print(usernames[name] + ' is the username of ' + name) # إذا لم يكن الاسم في القاموس else: # اطبع الرد print('I don\'t have ' + name + '\'s username, what is it?') # خذ اسم مستخدم جديد لربطه بذلك الاسم username = input() # name عين قيمة اسم المستخدم إلى المفتاح usernames[name] = username # اطبع ردًا يبيّن أنّ البيانات قد حُدّثت print('Data updated.') سننفِّذ البرنامج من سطر الأوامر: python usernames.py عندما ننفّذ البرنامج، سنحصل على مخرجات مشابهة لما يلي: Enter a name: Sammy sammy-shark is the username of Sammy Enter a name: Jesse I don't have Jesse's username, what is it? JOctopus Data updated. Enter a name: عند الانتهاء من اختبار البرنامج، اضغط على CTRL + C للخروج من البرنامج. يمكنك تخصيص حرف لإنهاء البرنامج (مثل الحرف q)، وجعل البرنامج يُنصت له عبر العبارات الشرطية. يوضح هذا المثال كيف يمكنك تعديل القواميس بشكل تفاعلي. في هذا البرنامج، بمجرد خروجك باستخدام CTRL + C، ستفقد جميع بياناتك، إلا إن خزّنت البيانات في ملف. يمكننا أيضًا إضافة عناصر إلى القواميس وتعديلها باستخدام التابع dict.update()‎. هذا التابع مختلف عن التابع append()‎ الذي يُستخدم مع القوائم. سنضيف المفتاح followers في القاموس jesse أدناه، ونَمنحه قيمة عددية صحيحة بواسطة التابع jesse.update()‎. بعد ذلك، سنطبع القاموس المُحدّث. jesse = {'username': 'JOctopus', 'online': False, 'points': 723} jesse.update({'followers': 481}) print(jesse) # {'followers': 481, 'username': 'JOctopus', 'points': 723, 'online': False} من المخرجات، نتبيّن أننا نجحنا في إضافة الزوج ‎followers: 481 إلى القاموس jesse. يمكننا أيضًا استخدام التابع dict.update()‎ لتعديل زوج قيمة-مفتاح موجود سلفًا عن طريق استبدال قيمة مفتاح معيَّن. سنغيِّر القيمة المرتبطة بالمفتاح online في القاموس Sammy من True إلى False: sammy = {'username': 'sammy-shark', 'online': True, 'followers': 987} sammy.update({'online': False}) print(sammy) # {'username': 'sammy-shark', 'followers': 987, 'online': False} يغيّر السطر sammy.update({'online': False})‎ القيمة المرتبطة بالمفتاح 'online' من True إلى False. عند استدعاء التابع print()‎‎ على القاموس، يمكنك أن ترى في المخرجات أنّ التحديث قد تمّ. لإضافة عناصر إلى القواميس أو تعديل القيم، يمكن إمّا استخدام الصياغة dict[key] = value، أو التابع dict.update()‎. حذف عناصر من القاموس كما يمكنك إضافة أزواج قيمة-مفتاح إلى القاموس، أو تغيير قيمه، يمكنك أيضًا حذف العناصر الموجودة في القاموس. لتزيل زوج قيمة-مفتاح من القاموس، استخدم الصياغة التالية: del dict[key] لنأخذ القاموس jesse الذي يمثل أحد المستخدمين، ولنفترض أنّ jesse لم تعد تستخدم المنصة لأجل ممارسة الألعاب، لذلك سنزيل العنصر المرتبط بالمفتاح points. بعد ذلك، سنطبع القاموس لتأكيد حذف العنصر: jesse = {'username': 'JOctopus', 'online': False, 'points': 723, 'followers': 481} del jesse['points'] print(jesse) # {'online': False, 'username': 'JOctopus', 'followers': 481} يزيل السطر del jesse ['points']‎ الزوج 'points': 723 من القاموس jesse. إذا أردت محو جميع عناصر القاموس، فيمكنك ذلك باستخدام التابع dict.clear()‎. سيَبقى هذا القاموس في الذاكرة، وهذا مفيد في حال احتجنا إلى استخدامه لاحقًا في البرنامج، بيْد أنه سيُفرِّغ جميع العناصر من القاموس. دعنا نزيل كل عناصر القاموس jesse: jesse = {'username': 'JOctopus', 'online': False, 'points': 723, 'followers': 481} jesse.clear() print(jesse) # {} تُظهِر المخرجات أنّ القاموسًا صار فارغًا الآن. إذا لم تعد بحاجة إلى القاموس، فاستخدم del للتخلص منه بالكامل: del jesse print(jesse) إذا نفّذت الأمر print()‎ بعد حذف القاموس jesse، سوف تتلقى الخطأ التالي: NameError: name 'jesse' is not defined خلاصة ألقينا في هذه المقالة نظرة على القواميس في بايثون. تتألف القواميس من أزواج قيمة-مفتاح، وتوفر حلًّا ممتازًا لتخزين البيانات دون الحاجة إلى فهرستها. يتيح لنا ذلك استرداد القيم بناءً على معانيها وعلاقتها بأنواع البيانات الأخرى. يمكنك تعلم المزيد عن أنواع البيانات الأخرى من المقالة التالية: "فهم أنواع البيانات في بايثون 3". هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة -وبتصرّف- للمقال Understanding Dictionaries in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة التالية: كيفية استيراد الوحدات في بايثون 3 المقالة السابقة: فهم نوع البيانات Tuples في بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  9. تُسِّهل البرمجة الكائنية كتابة شيفرات قابلة لإعادة الاستخدام وتجنب التكرار في مشاريع التطوير. إحدى الآليات التي تحقق بها البرمجة الكائنية هذا الهدف هي مفهوم الوراثة (inheritance)، التي بفضلها يمكن لصنفٍ فرعي (subclass) استخدام الشيفرة الخاصة بصنف أساسي (base class، ويطلق عليه «صنف أب» أيضًا) موجود مسبقًا. سيستعرض هذا الدرس بعض الجوانب الرئيسية لمفهوم الوراثة في بايثون، بما في ذلك كيفية إنشاء الأصناف الأساسية (parent classes) والأصناف الفرعية (child classes)، وكيفية إعادة تعريف (override) التوابع والخاصيات، وكيفية استخدام التابع ‎super()‎، وكيفية الاستفادة من الوراثة المتعددة (multiple inheritance). ما هي الوراثة؟ تقوم الوراثة على استخدام شيفرة صنف معين في صنف آخر أي يرث صنف يراد إنشاؤه شيفرة صنف آخر. يمكن تمثيل مفهوم الوراثة في البرمجة بالوراثة في علم الأحياء تمامًا، فالأبناء يرثون خاصيات معينة من آبائهم. ويمكن لطفل أن يرث طول والده أو لون عينيه بالإضافة إلى خاصيات أخرى جديدة خاصة فيه. كما يتشارك الأطفال نفس اسم العائلة الخاصة بآبائهم. ترث الأصناف الفرعية (subclasses، تُسمى أيضًا *الأصناف الأبناء [child classes]) التوابع والمتغيرات من *الأصناف الأساسية* (base classes، تسمى أيضًاالأصناف الآباء [parent classes]). مثلًا، قد يكون لدينا صنف أساسي يسمى ‎Parent‎ يحتوي متغيرات الأصناف ‎last_name‎ و ‎height‎ و ‎eye_color‎، والتي سيرثها الصنف الابن ‎Child‎. لمَّا كان الصنف الفرعي ‎Child‎ يرث الصنف الأساسي ‎Parent‎، فبإمكانه إعادة استخدام شيفرة ‎Parent‎، مما يسمح للمبرمج بكتابة شيفرة أوجز، وتقليل التكرار. الأصناف الأساسية تشكل الأصناف الأساسية أساسًا يمكن أن تستند إليه الأصناف الفرعية المُتفرِّعة منها، إذ تسمح الأصناف الأساسية بإنشاء أصناف فرعية عبر الوراثة دون الحاجة إلى كتابة نفس الشيفرة في كل مرة. يمكن تحويل أي صنف إلى صنف أساسي، إذ يمكن استخدامه لوحده، أو جعله قالبًا (نموذجًا). لنفترض أّنّ لدينا صنفًا أساسيًا باسم ‎Bank_account‎، وصنفين فرعيين مُشتقين منه باسم ‎Personal_account‎ و ‎Business_account‎. ستكون العديد من التوابع مشتركة بين الحسابات الشخصية (Personalaccount) والحسابات التجارية (Businessaccount)، مثل توابع سحب وإيداع الأموال، لذا يمكن أن تنتمي تلك التوابع إلى الصنف الأساسي ‎Bank_account‎. سيكون للصنف ‎Business_account‎ توابع خاصة به، مثل تابع مخصص لعملية جمع سجلات ونماذج الأعمال، بالإضافة إلى متغير ‎employee_identification_number‎ موروث من الصنف الأب. وبالمثل، قد يحتوي الصنف ‎Animal‎ على التابعين ‎eating()‎ و ‎sleeping()‎، وقد يتضمن الصنف الفرعي ‎Snake‎ تابعين إضافيين باسم ‎hissing()‎ و ‎slithering()‎ خاصين به. دعنا ننشئ صنفًا أساسيًا باسم ‎Fish‎ لاستخدامه لاحقًا أساسًا لأصناف فرعية تمثل أنواع الأسماك. سيكون لكل واحدة من تلك الأسماك أسماء أولى وأخيرة، بالإضافة إلى خصائص مميزة خاصة بها. سننشئ ملفًا جديدًا يسمى ‎fish.py‎ ونبدأ بالباني، والذي سنعرّف داخله متغيري الصنف ‎first_name‎ و ‎last_name‎ لكل كائنات الصنف ‎Fish‎، أو أصنافه الفرعية. class Fish: def __init__(self, first_name, last_name="Fish"): self.first_name = first_name self.last_name = last_name القيمة الافتراضية للمتغير ‎last_name‎ هي السلسلة النصية ‎"Fish"‎، لأننا نعلم أنّ معظم الأسماك سيكون هذا هو اسمها الأخير. لنُضف بعض التوابع الأخرى: class Fish: def __init__(self, first_name, last_name="Fish"): self.first_name = first_name self.last_name = last_name def swim(self): print("The fish is swimming.") def swim_backwards(self): print("The fish can swim backwards.") لقد أضفنا التابعين ‎swim()‎ و ‎swim_backwards()‎ إلى الصنف ‎Fish‎ حتى يتسنى لكل الأصناف الفرعية استخدام هذه التوابع. ما دام أنّ معظم الأسماك التي ننوي إنشاءها ستكون عظمية (أي أنّ لها هيكلا عظميًا) وليس غضروفية (أي أن لها هيكلًا غضروفيًا)، فيمكننا إضافة بعض الخاصيات الإضافية إلى التابع ‎__init__()‎: class Fish: def __init__(self, first_name, last_name="Fish", skeleton="bone", eyelids=False): self.first_name = first_name self.last_name = last_name self.skeleton = skeleton self.eyelids = eyelids def swim(self): print("The fish is swimming.") def swim_backwards(self): print("The fish can swim backwards.") لا يختلف بناء الأصناف الأساسية عن بناء أي صنف آخر، إلا أننا نصممها لتستفيد منها الأصناف الفرعية المُعرّفة لاحقًا. الأصناف الفرعية الأصناف الفرعية هي أصناف ترث كل شيء من الصنف الأساسي. هذا يعني أنّ الأصناف الفرعية قادرة على الاستفادة من توابع ومتغيرات الصنف الأساسي. على سبيل المثال، سيتمكن الصنف الفرعي ‎Goldfish‎ المشتق من الصنف ‎Fish‎ من استخدام التابع ‎swim()‎ المُعرّف في ‎Fish‎ دون الحاجة إلى التصريح عنه. يمكننا النظر إلى الأصناف الفرعية على أنها أقسام من الصنف الأساسي. فإذا كان لدينا صنف فرعي يسمى ‎Rhombus‎ (معيّن)، وصنف أساسي يسمى ‎Parallelogram‎ (متوازي الأضلاع)، يمكننا القول أنّ المعين (‎Rhombus‎) هو متوازي أضلاع (‎Parallelogram‎). يبدو السطر الأول من الصنف الفرعي مختلفًا قليلًا عن الأصناف غير الفرعية، إذ يجب عليك تمرير الصنف الأساسي إلى الصنف الفرعي كمعامل: class Trout(Fish): الصنف ‎Trout‎ هو صنف فرعي من ‎Fish‎. يدلنا على هذا الكلمةُ ‎Fish‎ المُدرجة بين قوسين. يمكننا إضافة توابع جديدة إلى الأصناف الفرعية، أو إعادة تعريف التوابع الخاصة بالصنف الأساسي، أو يمكننا ببساطة قبول التوابع الأساسية الافتراضية باستخدام الكلمة المفتاحية ‎pass‎، وهو ما سنفعله في المثال التالي: ... class Trout(Fish): pass يمكننا الآن إنشاء كائن من الصنف ‎Trout‎ دون الحاجة إلى تعريف أي توابع إضافية. ... class Trout(Fish): pass terry = Trout("Terry") print(terry.first_name + " " + terry.last_name) print(terry.skeleton) print(terry.eyelids) terry.swim() terry.swim_backwards() لقد أنشأنا كائنًا باسم ‎terry‎ من الصنف ‎Trout‎، والذي سيستخدم جميع توابع الصنف ‎Fish‎ وإن لم نعرّفها في الصنف الفرعي ‎Trout‎. يكفي أن نمرر القيمة ‎"Terry"‎ إلى المتغير ‎first_name‎، أما المتغيرات الأخرى فقد جرى تهيئتها سلفًا. عند تنفيذ البرنامج، سنحصل على المخرجات التالية: Terry Fish bone False The fish is swimming. The fish can swim backwards. لننشئ الآن صنفًا فرعيًا آخر يعرّف تابعًا خاصا به. سنسمي هذا الصنف ‎Clownfish‎. سيسمح التابع الخاص به بالتعايش مع شقائق النعمان البحري: ... class Clownfish(Fish): def live_with_anemone(self): print("The clownfish is coexisting with sea anemone.") دعنا ننشئ الآن كائنًا آخر من الصنف ‎Clownfish‎: ... casey = Clownfish("Casey") print(casey.first_name + " " + casey.last_name) casey.swim() casey.live_with_anemone() عند تنفيذ البرنامج، سنحصل على المخرجات التالية: Casey Fish The fish is swimming. The clownfish is coexisting with sea anemone. تُظهر المخرجات أنّ الكائن ‎casey‎ المستنسخ من الصنف ‎Clownfish‎ قادر على استخدام التابعين ‎__init__()‎ و ‎swim()‎ الخاصين بالصنف ‎Fish‎، إضافة إلى التابع ‎live_with_anemone()‎ الخاص بالصنف الفرعي. إذا حاولنا استخدام التابع ‎live_with_anemone()‎ في الكائن ‎Trout‎، فسوف يُطلق خطأ: terry.live_with_anemone() AttributeError: 'Trout' object has no attribute 'live_with_anemone' ذلك أنَّ التابع ‎live_with_anemone()‎ ينتمي إلى الصنف الفرعي ‎Clownfish‎ فقط، وليس إلى الصنف الأساسي ‎Fish‎. ترث الأصناف الفرعية توابع الصنف الأساسي الذي اشتُقَّت منه، لذا يمكن لكل الأصناف الفرعية استخدام تلك التوابع. إعادة تعريف توابع الصنف الأساسي في المثال السابق عرّفنا الصنف الفرعي ‎Trout‎ الذي استخدم الكلمة المفتاحية ‎pass‎ ليرث جميع سلوكيات الصنف الأساسي ‎Fish‎، وعرّفنا كذلك صنفًا آخر ‎Clownfish‎ يرث جميع سلوكيات الصنف الأساسي، ويُنشئ أيضًا تابعًا خاصًا به. قد نرغب في بعض الأحيان في استخدام بعض سلوكيات الصنف الأساسي، ولكن ليس كلها. يُطلَق على عملية تغيير توابع الصنف الأساسي «إعادة التعريف» (Overriding). عند إنشاء الأصناف الأساسية أو الفرعية، فلا بد أن تكون لك رؤية عامة لتصميم البرنامج حتى لا تعيد تعريف التوابع إلا عند الضرورة. سننشئ صنفًا فرعيًا ‎Shark‎ مشتقًا من الصنف الأساسي ‎Fish‎، الذي سيمثل الأسماك العظمية بشكل أساسي، لذا يتعين علينا إجراء تعديلات على الصنف ‎Shark‎ المخصص في الأصل للأسماك الغضروفية. من منظور تصميم البرامج، إذا كانت لدينا أكثر من سمكة غير عظمية واحدة، فيُستحب أن ننشئ صنفًا خاصًا بكل نوع من هذين النوعين من الأسماك. تمتلك أسماك القرش، على عكس الأسماك العظمية، هياكل مصنوعة من الغضاريف بدلاً من العظام. كما أنّ لديها جفونًا، ولا تستطيع السباحة إلى الوراء، كما أنها قادرة على المناورة للخلف عن طريق الغوص. على ضوء هذه المعلومات، سنعيد تعريف الباني ‎__init__()‎ والتابع ‎swim_backwards()‎. لا نحتاج إلى تعديل التابع ‎swim()‎ لأنّ أسماك القرش يمكنها السباحة. دعنا نلقي نظرة على هذا الصنف الفرعي: ... class Shark(Fish): def __init__(self, first_name, last_name="Shark", skeleton="cartilage", eyelids=True): self.first_name = first_name self.last_name = last_name self.skeleton = skeleton self.eyelids = eyelids def swim_backwards(self): print("The shark cannot swim backwards, but can sink backwards.") لقد أعدنا تعريف المعاملات التي تمت تهيئتها في التابع ‎__init__()‎، فأخذ المتغير ‎last_name‎ القيمة ‎"Shark"‎، كما أُسنِد إلى المتغير ‎skeleton‎ القيمة ‎"cartilage"‎، فيما أُسنِدَت القيمة المنطقية ‎True‎ إلى المتغير ‎eyelids‎. يمكن لجميع نُسخ الصنف إعادة تعريف هذه المعاملات. يطبع التابع ‎swim_backwards()‎ سلسلة نصية مختلفة عن تلك التي يطبعها في الصنف الأساسي ‎Fish‎، لأنّ أسماك القرش غير قادرة على السباحة للخلف كما تفعل الأسماك العظمية. يمكننا الآن إنشاء نسخة من الصنف الفرعي ‎Shark‎، والذي سيستخدم التابع ‎swim()‎ الخاص بالصنف الأساسي ‎Fish‎: ... sammy = Shark("Sammy") print(sammy.first_name + " " + sammy.last_name) sammy.swim() sammy.swim_backwards() print(sammy.eyelids) print(sammy.skeleton) عند تنفيذ هذه الشيفرة، سنحصل على المخرجات التالية: Sammy Shark The fish is swimming. The shark cannot swim backwards, but can sink backwards. True cartilage لقد أعاد الصنف الفرعي ‎Shark‎ تعريف التابعين ‎__init__()‎ و ‎swim_backwards()‎ الخاصين بالصنف الأساسي ‎Fish‎، وورث في نفس الوقت التابع ‎swim()‎ الخاص بالصنف الأساسي. الدالة ‎super()‎ يمكنك باستخدام الدالة ‎super()‎ الوصول إلى التوابع الموروثة التي أُعيدت كتابتها. عندما نستخدم الدالة ‎super()‎، فإننا نستدعي التابع الخاص بالصنف الأساسي لاستخدامه في الصنف الفرعي. على سبيل المثال، قد نرغب في إعادة تعريف جانب من التابع الأساسي وإضافة وظائف معينة إليه، ثم بعد ذلك نستدعي التابع الأساسي لإنهاء بقية العمل. في برنامج خاص بتقييم الطلاب مثلًا، قد نرغب في تعريف صنف فرعي ‎Weighted_grade‎ يرث الصنف الأساسي ‎Grade‎، ونعيد فيه تعريف التابع ‎calculate_grade()‎ الخاص بالصنف الأساسي من أجل تضمين شيفرة خاصة بحساب التقدير المرجّح (weighted grade)، مع الحفاظ على بقية وظائف الصنف الأساسي. عبر استدعاء التابع ‎super()‎، سنكون قادرين على تحقيق ذلك. عادة ما يُستخدم التابع ‎super()‎ ضمن التابع ‎__init__()‎، لأنّه المكان الذي ستحتاج فيه على الأرجح إلى إضافة بعض الوظائف الخاصة إلى الصنف الفرعي قبل إكمال التهيئة من الصنف الأساسي. لنضرب مثلًا لتوضيح ذلك، دعنا نعدّل الصنف الفرعي ‎Trout‎. نظرًا لأنّ سمك السلمون المرقَّط من أسماك المياه العذبة، فلنضف متغيرًا اسمه ‎water‎ إلى التابع ‎__init__()‎، ولنُعطه القيمة ‎"freshwater"‎، ولكن مع الحفاظ على باقي متغيرات ومعاملات الصنف الأساسي: ... class Trout(Fish): def __init__(self, water = "freshwater"): self.water = water super().__init__(self) ... لقد أعدنا تعريف التابع ‎__init__()‎ في الصنف الفرعي ‎Trout‎، وغيرنا سلوكه موازنةً بالتابع ‎__init__()‎ المُعرَّف سلفًا في الصنف الأساسي ‎Fish‎. لاحظ أننا استدعينا التابع ‎__init__()‎ الخاص بالصنف ‎Fish‎ بشكل صريح ضمن التابع ‎__init__()‎ الخاص بالصنف ‎Trout‎،. بعد إعادة تعريف التابع، لم نعد بحاجة إلى تمرير ‎first_name‎ كمعامل إلى ‎Trout‎، وفي حال فعلنا ذلك، فسيؤدي ذلك إلى إعادة تعيين ‎freshwater‎ بدلاً من ذلك. سنُهيِّئ بعد ذلك الخاصية ‎first_name‎ عن طريق استدعاء المتغير في الكائن خاصتنا. الآن يمكننا استدعاء متغيرات الصنف الأساسي التي تمت تهيئتها، وكذلك استخدام المتغير الخاص بالصنف الفرعي: ... terry = Trout() # تهيئة الاسم الأول terry.first_name = "Terry" # super() الخاص بالصنف الأساسي عبر __init__() استخدام print(terry.first_name + " " + terry.last_name) print(terry.eyelids) # المعاد تعريفها في الصنف الفرعي __init__() استخدام print(terry.water) # الخاص بالصنف الأساسي swim() استخدام التابع terry.swim() سنحصل على المخرجات التالية: Terry Fish False freshwater The fish is swimming. تُظهر المخرجات أنّ الكائن ‎terry‎ المنسوخ من الصنف الفرعي ‎Trout‎ قادر على استخدام المتغير ‎water‎ الخاص بتابع الصنف الفرعي ‎__init__()‎، إضافة إلى استدعاء المتغيرات ‎first_name‎ و ‎last_name‎ و ‎eyelids‎ الخاصة بالتابع ‎__init__()‎ المُعرَّف في الصنف الأساسي ‎Fish‎. يسمح لنا التابع ‎super()‎ المُضمن في بايثون باستخدام توابع الصنف الأساسي حتى بعد إعادة تعريف تلك التوابع في الأصناف الفرعية. الوراثة المُتعدِّدة (Multiple Inheritance) المقصود بالوراثة المتعددة هي قدرة الصنف على أن يرث الخاصيات والتوابع من أكثر من صنف أساسي واحد. هذا من شأنه تقليل التكرار في البرامج، ولكنه يمكن أيضًا أن يُعقِّد العمل، لذلك يجب استخدام هذا المفهوم بحذر. لإظهار كيفية عمل الوراثة المتعددة، دعنا ننشئ صنفًا فرعيًا ‎Coral_reef‎ يرث من الصنفين ‎Coral‎ و ‎Sea_anemone‎. يمكننا إنشاء تابع في كل صنف أساسي، ثم استخدام الكلمة المفتاحية ‎pass‎ في الصنف الفرعي ‎Coral_reef‎: class Coral: def community(self): print("Coral lives in a community.") class Anemone: def protect_clownfish(self): print("The anemone is protecting the clownfish.") class CoralReef(Coral, Anemone): pass يحتوي الصنف ‎Coral‎ على تابع يسمى ‎community()‎، والذي يطبع سطرًا واحدًا، بينما يحتوي الصنف ‎Anemone‎ على تابع يسمى ‎protect_clownfish()‎، والذي يطبع سطرًا آخر. سنُمرِّر الصنفين كلاهما بين قوسين في تعريف الصنف CoralReef، ما يعني أنه سيرث الصنفين معًا. دعنا الآن ننشئ كائنًا من الصنف CoralReef: ... great_barrier = CoralReef() great_barrier.community() great_barrier.protect_clownfish() الكائن ‎great_barrier‎ مُشتقٌ الصنف ‎CoralReef‎، ويمكنه استخدام التوابع من كلا الصنفين الأساسيين. عند تنفيذ البرنامج، سنحصل على المخرجات التالية: Coral lives in a community. The anemone is protecting the clownfish. تُظهِر المخرجات أنَّ التوابع من كلا الصنفين الأساسيين استُخدِما بفعالية في الصنف الفرعي. تسمح لنا الوراثة المُتعدِّدة بإعادة استخدام الشيفرات البرمجية المكتوبة في أكثر من صنف أساسي واحد. وإذا تم تعريف التابع نفسه في أكثر من صنف أساسي واحد، فإنّ الصنف الفرعي سيستخدم التابع الخاص بالصنف الأساسي الذي ظهر أولًا في قائمة الأصناف المُمرَّرة إليه عند تعريفه. رغم فوائدها الكثيرة وفعاليتها، إلا أنَّ عليك توخي الحذر في استخدام الوراثة المُتعدِّدة، حتى لا ينتهي بك الأمر بكتابة برامج مُعقَّدة وغير مفهومة للمبرمجين الآخرين. خلاصة تعلمنا في هذا الدرس كيفية إنشاء أصناف أساسية وفرعية، وكيفية إعادة تعريف توابع وخاصيات الأصناف الأساسية داخل الأصناف الفرعية باستخدام التابع ‎super()‎، إضافة إلى مفهوم الوراثة المتعددة. الوراثة هي إحدى أهم ميزات البرمجة الكائنية التي تجعلها متوافقة مع مبدأ DRY (لا تكرر نفسك)، وهذا يحسن إنتاجية المبرمجين، ويساعدهم على تصميم برامج فعالة وواضحة. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة -وبتصرّف- للمقال Understanding Class Inheritance in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة التالية: كيفية تطبيق التعددية الشكلية (Polymorphism) على الأصناف المقالة السابقة: فهم متغيرات الأصناف والنسخ في بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  10. الأعداد شائعةٌ جدًا في البرمجة، إذ تُستخدم لتمثيل مختلف القيم، مثل أبعاد حجم الشاشة، والمواقع الجغرافية، والمبالغ المالية، ومقدار الوقت الذي مر منذ بداية فيديو، والألوان وغير ذلك. table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } تعد القدرة على تنفيذ العمليات الرياضية بفعالية في البرمجة مهارة مهمة، لأنّك ستعمل على الأعداد كثيرًا. الفهم الجيد للرياضيات يمكن أن يساعدك على أن تصبح مبرمجًا أفضل، إلا أنه ليس شرطًا أساسيًا. فالرياضيات أداة لتحقيق ما ترغب في تحقيقه، وطريقة لتحسين خططك. سنعمل مع أكثر نوعي البيانات استخدامًا في بايثون، وهما الأعداد الصحيحة والأعداد العشرية: الأعداد الصحيحة هي أعداد كاملة يمكن أن تكون موجبة أو سالبة أو معدومة (...، ‎-1، 0، 1، ...). الأعداد العشرية هي أعداد حقيقية تحتوي على فاصلة عشرية (كما في 9.0 أو ‎-2.25). سنلقي في هذه المقالة نظرة على العوامل (operators) التي يمكن استخدامها مع أنواع البيانات العددية في بايثون. العوامل العامل (operator) هو رمز أو دالة تمثل عملية حسابية. على سبيل المثال، في الرياضيات، علامة الجمع أو + هي العامل الذي يشير إلى عملية الجمع. في بايثون، سنرى بعض العوامل المألوفة، والتي استُعيرَت من الرياضيات، لكن هناك عوامل أخرى خاصة بمجال البرمجة. الجدول التالي مرجعٌ سريعٌ للعوامل الحسابية في بايثون. سنغطي جميع هذه العمليات في مقالتنا هذه. العملية الناتج x + y مجموع x مع y x - y طرح x من y ‎-x تغيير إشارة x ‎+x قيمة x نفسها x * y ضرب x بـ y x / y قسمة x على y x // y حاصل القسمة التحتية لـ x على y x % y باقي قسمة x على y x ** y قيمة x مرفوعةً للقوة y سنتحدث أيضًا عن عوامل الإسناد المركبة (compound assignment operators)، بما في ذلك ‎+=‎ و ‎*=‎، التي تجمع عاملًا حسابيًا مع العامل =. الجمع والطرح في بايثون، يعمل عاملا الجمع والطرح كما هو معروف في الرياضيات. في الواقع، يمكنك استخدام لغة بايثون آلةً حاسبةً. لنلقِ نظرة على بعض الأمثلة، بدءًا من الأعداد الصحيحة: print(1 + 5) والناتج: 6 بدلًا من تمرير أعداد صحيحة مباشرة إلى الدالة print، يمكننا تهيئة المتغيرات بأعداد صحيحة: a = 88 b = 103 print(a + b) وسينتج لنا: 191 الأعداد الصحيحة يمكن أن تكون موجبة أو سالبة (أو معدومة أيضًا)، لذلك يمكننا إضافة عدد سالب إلى عدد موجب: c = -36 d = 25 print(c + d) # -11 الجمع سيكون مشابهًا مع الأعداد العشرية: e = 5.5 f = 2.5 print(e + f) # 8.0 إذا جمعنا عددين عشريين معًا، ستعيد بايثون عددًا عشريًّا. صياغة الطرح تشبه صياغة الجمع، ما عدا أنك ستستبدل بعامل الطرح (-) عامل الجمع (+? g = 75.67 h = 32 print(g - h) # 43.67 هنا، طرحنا عددًا صحيحًا من عدد عشري. ستعيد بايثون عددًا عشريًّا إذا كان أحد الأعداد المتضمنة في المعادلة عشريًّا. دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن العمليات الحسابية الأحادية يتكون التعبير الرياضي الأحادي (unary mathematical expression) من مكوّن أو عنصر واحد فقط، ويمكن في بايثون استخدام العلامتين + و - بمفردهما عبر اقترانهما بقيمة لإعادة القيمة نفسها (+)، أو تغيير إشارة القيمة (-) . رغم أنها لا تُستخدم كثيرًا، تشير علامة الجمع إلى هوية القيمة (identity of the value)، أي تعيد القيمة نفسها. يمكننا استخدام علامة الجمع مع القيم الموجبة: i = 3.3 print(+i) # 3.3 عندما نستخدم علامة الجمع مع قيمة سالبة، فستُعيد القيمة نفسها، وفي هذه الحالة ستكون قيمة سالبة: j = -19 print(+j) # -19 عند استخدامها مع قيمة سالبة، ستعيد علامة الجمع القيمة السالبة نفسها. علامة الطرح، على خلاف علامة الجمع، تغيّر إشارة القيمة. لذلك، عندما نضعها مع قيمة موجبة، ستُعاد القيمة السالبة منها: i = 3.3 print(-i) # -3.3 بالمقابل، عندما نستخدم عامل الطرح الأحادي (minus sign unary operator) مع قيمة سالبة، فستُعاد القيمة الموجبة منها: j = -19 print(-j) # 19 ستُعيِد العمليتان الحسابيتان الأحاديتان + و - إمَّا هوية القيمة المعطاة، أو القيمة المعاكسة في الإشارة للقيمة المعطاة على التوالي. الضرب والقسمة مثل الجمع والطرح، الضرب والقسمة في بايثون مشابهان لما هو معروف في الرياضيات. علامة الضرب في بايثون هي *، وعلامة القسمة هي /. فيما يلي مثال على ضرب عددين عشريين في بايثون: k = 100.1 l = 10.1 print(k * l) # 1011.0099999999999 عندما تُجري عملية القسمة في بايثون 3، فسيكون العدد المُعاد دائمًا عشريًّا، حتى لو استخدمت عددين صحيحين: m = 80 n = 5 print(m / n) # 16.0 هذا أحد الاختلافات الرئيسية بين بايثون 2 و بايثون 3. الإجابة في بايثون 3 تكون كسرية، فعند استخدام / لتقسيم 11 على 2 مثلًا، فستُعاد القيمة 5.5. أمَّا في بايثون 2، فحاصل التعبير 11/2 هو 5. يُجرِي العامل / في بايثون 2 قسمة تحتية (floor division)، إذ أنّه إن كان حاصل القسمة يساوي x، فسيكون ناتج عملية القسمة في بايثون 2 أكبر عدد من الأعداد الصحيحة الأصغر من أو تساوي x. إذا نفَّذت المثال print(80 / 5)‎ أعلاه في بايثون 2 بدلًا من بايثون 3، فسيكون الناتج هو 16، وبدون الجزء العشري. في بايثون 3، يمكنك استخدام العامل // لإجراء القسمة التحتية. التعبير ‎100 // 40‎ سيعيد القيمة 2. القسمة التحتية مفيدة في حال كنت تريد أن يكون حاصل القسمة عددًا صحيحًا. معامل باقي القسمة (Modulo) العامل % هو عامل الباقي (modulo)، والذي يُرجع باقي عملية القسمة. هذا مفيد للعثور على الأعداد التي هي مضاعفات لنفس العدد، على سبيل المثال. المثال التالي يوضح كيفية استخدام عامل الباقي: o = 85 p = 15 print(o % p) # 10 حاصل قسمة 85 على 15 هو 5، والباقي 10. القيمة 10 هي التي ستُعاد هنا لأنَّ عامل الباقي يعيد باقي عملية القسمة. إذا استخدمنا عددين عشريين مع عامل الباقي، فسيُعاد عدد عشري: q = 36.0 r = 6.0 print(o % p) # 0.0 في حال قسمة 36.0 على 6.0، فلن يكون هناك باقٍ، لذلك تعاد القيمة 0.0. القوة (Power) يُستخدم عامل القوة ** (يقال له أحيًانًا «الأس») في بايثون لرفع العدد على اليسار لقوة الأس على اليمين. وهذا يعني أنه في التعبير 5 ** 3، العدد 5 سيُرفع إلى القوة 3. في الرياضيات، غالبًا ما نرى هذا التعبير يُكتب على الشكل 5³، حيث يُضرب العدد 5 في نفسه 3 مرات. في بايثون، التعبيران 5 ** 3 و 5 * 5 * 5 سيعطيان النتيجة نفسها. سنستخدم في المثال التالي المتغيرات: s = 52.25 t = 7 print(s ** t) # 1063173305051.292 رفع العدد العشري 52.25 إلى القوة 7 عبر عامل الأسّ ** سينتج عنه عدد عشري كبير. أسبقية العوامل في بايثون، كما هو الحال في الرياضيات، علينا أن نضع في حساباتنا أنَّ العوامل ستُقيَّم وفقًا لنظام الأسبقية، وليس من اليسار إلى اليمين، أو من اليمين إلى اليسار. إذا نظرنا إلى التعبير التالي: u = 10 + 10 * 5 قد نقرأه من اليسار إلى اليمين، ولكن تذكّر أنّ عملية الضرب ستُجرى أولًا، لذا إن استدعينا print(u)‎، فسنحصل على القيمة التالية: 60 هذا لأنّ 10 * 5 ستُقيّم أولًا، وسينتج عنها العدد 50، ثم نضيف إليها 10 لنحصل على 60 كنتيجة نهائية. إذا أردنا بدلًا من ذلك إضافة القيمة 10 إلى 10، ثم ضرب المجموع في 5، فيمكننا استخدام الأقواس كما نفعل في الرياضيات: u = (10 + 10) * 5 print(u) # 100 إحدى الطرق البسيطة لتذكر الأسبقيات هي حفظ الجملتين «قم أبعد ضيقًا ... قم جد طريقًا» لتذكر أوائل كلماتهما: الأسبقية الحرف المعنى 1 قم أقواس 2 أبعد الأس 3 ضيقًا الضرب 4 قم القسمة 5 جد الجمع 6 طريقًا الطرح ملاحظة: انتبه إلى أن الضرب ليس بالضرورة قبل القسمة كما هو مذكور في الجدول، بل أيهما أسبق بالمعادلة (في حال لم تكن هنالك أية أقواس)، فإن جاءت عملية القسمة قبل الضرب، فستُنفَّذ أولًا وفقًا لذلك الترتيب (ترتيب التنفيذ من اليسار إلى اليمين)، وكذلك الأمر بالنسبة لعملية الجمع والطرح، فليس الجمع دائما قبل الطرح، بل أيهما أسبق. u = (10 / 5 * 2) - 3 + 4 print(u) # 5 عامل الإسناد (Assignment Operators) أكثر عوامل الإسناد استخدامًا هو إشارة التساوي =. يُسنِد عامل الإسناد (أو عامل التعيين) = القيمة الموجودة على اليمين إلى المتغير الموضوع على اليسار. على سبيل المثال، يُسنِد التعبير v = 23 العدد الصحيح 23 للمتغير v. من الشائع استخدام عوامل الإسناد المركبة التي تجري عملية رياضية على قيمة المتغير، ثم تُسنِد القيمة الجديدة الناتجة إلى ذلك المتغير. تجمع عوامل الإسناد المركبة بين عامل رياضي والعامل =؛ ففي حال الجمع، نستخدم + مع = للحصول على عامل الإسناد المركب ‎+=‎. لنطبِّق ذلك في مثال عملي: w = 5 w += 1 print(w) # 6 أولاً، نعيّن المتغير ‎w إلى القيمة 5، ثم نستخدم عامل الإسناد المركب ‎+=‎ لإضافة العدد الصحيح إلى قيمة المتغير الأيسر، ثم نُسنِد النتيجة إلى المتغير w. تُستخدم عوامل الإسناد المركبة بشكل متكرر مع حلقات for التكرارية، والتي ستستخدمها عندما تريد تكرار عمليةٍ عدة مرات: for x in range (0, 7): x *= 2 print(x) والناتج سيكون: 0 2 4 6 8 10 12 باستخدام الحلقة for، تمكنا من أتمتة العملية ‎*=‎ التي تضرب قيمة المتغير w بالعدد 2، ثم تُسنِد النتيجة إلى المتغير w لأجل استخدامها في التكرار التالي في الحلقة. لدى بايثون عامل إسناد مركب مقابل لكل من العوامل الحسابية التي تم التطرق إليها في هذه المقالة: y += 1 # إضافة القيمة ثم إسنادها y -= 1 # طرح القيمة ثم إسنادها y *= 2 # ضرب القيمة ثم إسنادها y /= 3 # تقسيم القيمة ثم إسنادها y // = 5 # تقسيم سفلي القيمة ثم إسنادها y **= 2 # تنفيذ عامل الأس على القيمة ثم إسنادها y %= 3 # إعادة باقي قسمة القيمة ثم إسناده يمكن أن يكون عامل الإسناد المركب مفيدًا عندما تحتاج إلى الزيادة أو الإنقاص التدريجي، أو عندما تحتاج إلى أتمتة عمليات معينة في برنامجك. خلاصة غطّينا في هذه المقالة العديد من العوامل التي ستستخدمها مع الأعداد الصحيحة والعشرية. يمكنك قراءة المزيد عن الأعداد في المقالة: الدوال العددية المضمّنة في بايثون 3. لمعرفة المزيد حول أنواع البيانات الأخرى، ألق نظرة على المقالة: فهم أنواع البيانات في بايثون 3، وتعرف على كيفية تحويل أنواع البيانات في المقالة: كيفية تحويل أنواع البيانات في بايثون 3. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة -وبتصرّف- للمقال How To Do Math in Python 3 with Operators لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة التالية: الدوال الرياضية المضمّنة في بايثون 3 المقالة السابقة: كيفية استخدام آلية تنسيق السلاسل النصية صفحة: تعلم بايثون المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  11. بايثون لغةٌ برمجة كائنية (object-oriented programming language). تركز البرمجة الكائنية (OOP) على كتابة شيفرات قابلة لإعادة الاستخدام، على عكس البرمجة الإجرائية (procedural programming) التي تركز على كتابة تعليمات صريحة ومتسلسلة. تتيح البرمجة الكائنية لمبرمجي بايثون كتابة شيفرات سهلة القراءة والصيانة، وهذا مفيد للغاية عند تطوير البرامج المُعقَّدة. التمييز بين الأصناف والكائنات أحدُ المفاهيم الأساسية في البرمجة الكائنية، ويوضح التعريفان التاليان الفرق بين المفهومين: الصنف - نموذج عام تُنسج على منواله كائنات يُنشِئها المبرمج. يُعرِّف الصنف مجموعةً من الخاصيات التي تميز أي كائن يُستنسَخ (instantiated) منه. الكائن - نسخةٌ (instance) من الصنف، فهو تجسيد عملي للصنف داخل البرنامج. تُستخدَم الأصناف لإنشاء أنماط، ثم تُستعمَل تلك الأنماط لإنشاء كائنات منها. ستتعلم في هذا الدرس كيفيَّة إنشاء الأصناف والكائنات، وتهيئة الخاصيات باستخدام تابعٍ بانٍ (constructor method)، والعمل على أكثر من كائن من نفس الصنف. الأصناف الأصناف هي نماذج عامة تُستخدم لإنشاء كائنات وسبق أن عرَّفناها آنفًا. تُنشَأ الأصناف باستخدام الكلمة المفتاحية ‎class‎، بشكل مشابه [لتعريف الدوال](رابط المقالة 34) الذي يكون باستخدام الكلمة المفتاحية ‎def‎. دعنا نعرّف صنفًا يسمى ‎Shark‎، ونجعل له تابعين مرتبطين به، swim و be_awesome: class Shark: def swim(self): print("The shark is swimming.") def be_awesome(self): print("The shark is being awesome.") تُسمَّى مثل هذه الدوال «توابعًا» (methods) لأنهما معرفتان داخل الصنف ‎Shark‎؛ أي أنهما دالتان تابعتان للصنف ‎Shark‎. الوسيط الأول لهاتَين الدالتين هو ‎self‎، وهو مرجع إلى الكائنات التي يتم بناؤها من هذا الصنف. للإشارة إلى نُسخ (أو كائنات) من الصنف، يوضع ‎self‎ دائمًا في البداية، لكن يمكن أن تكون معه وسائط أخرى. لا يؤدي تعريف الصنف ‎Shark‎ إلى إنشاء كائنات منه، وإنما يعرّف فقط النمط العام لتلك الكائنات، والتي يمكننا تعريفها لاحقًا. لذا، إذا نفّذت البرنامج أعلاه الآن، فلن يُعاد أي شيء. الكائنات الكائن هو نسخةٌ (instance) من صنف. ويمكن أن نأخذ الصنف ‎Shark‎ المُعرَّف أعلاه، ونستخدمه لإنشاء كائن يعدُّ نسخةً منه. سننشئ كائنًا ‎Shark‎ يسمى ‎sammy‎: sammy = Shark() لقد أحلنا على الكائن ‎sammy‎ ناتج الباني ‎Shark()‎، والذي يعيد نسخةً من الصنف. سنستخدم في الشيفرة التالية التابعين الخاصين بالكائن ‎sammy‎: sammy = Shark() sammy.swim() sammy.be_awesome() يستخدم الكائن ‎sammy‎ التابعين ‎swim()‎ و ‎be_awesome()‎، وقد استدعينَاهما باستعمال المعامل النقطي (‎.‎)، والذي يُستخدم للإشارة إلى خاصيات أو توابع الكائنات. في هذه الحالة، استدعينا تابعًا، لذلك استعملنا قوسين مثلما نفعل عند استدعاء دالة. الكلمة ‎self‎ هي معامل يُمرّر إلى توابع الصنف ‎Shark‎، في المثال أعلاه، يمثّل ‎self‎ الكائن ‎sammy‎. يتيح المعامل ‎self‎ للتوابع الوصول إلى خاصيات الكائن الذي استُدعيت معه. لاحظ أننا لم نمرر شيئًا داخل القوسين عند استدعاء التابع أعلاه، ذلك أنّ الكائن ‎sammy‎ يُمرّر تلقائيًا مع العامل النقطي. البرنامج التالي يوضح لنا الأمر: class Shark: def swim(self): print("The shark is swimming.") def be_awesome(self): print("The shark is being awesome.") def main(): sammy = Shark() sammy.swim() sammy.be_awesome() if __name__ == "__main__": main() لننفذ البرنامج لنرى ما سيحدث: python shark.py ستُطبع المخرجات التالية: The shark is swimming. The shark is being awesome. في الشيفرة أعلاه، استدعى الكائن ‎sammy‎ التابعين ‎swim()‎ و ‎be_awesome()‎ في الدالة الرئيسية ‎main()‎. الباني يٌستخدم الباني (Constructor Method) لتهيئة البيانات الأولية، ويُنفَّذ لحظة إنشاء الكائن. في تعريف الصنف، يأخذ الباني الاسم ‎__init__‎، وهو أول تابع يُعرّف في الصنف، ويبدو كما يلي: class Shark: def __init__(self): print("This is the constructor method.") إذا أضفت التابع ‎__init__‎ إلى الصنف ‎Shark‎ في البرنامج أعلاه، فسيَطبع البرنامجُ المخرجات التالية: This is the constructor method. The shark is swimming. The shark is being awesome. يُنفَّذ الباني تلقائيًا، لذا يستخدمه مطورو بايثون لتهيئة أصنافهم. سنُعدِّل الباني أعلاه، ونجعله يستخدم متغيرًا اسمه ‎name‎ سيمثّل اسم الكائن. في الشيفرة التالية، سيكون المتغير ‎name‎ المعامل المُمرَّر إلى الباني، ونحيل قيمته إلى الخاصية ‎self.name‎: class Shark: def __init__(self, name): self.name = name بعد ذلك، يمكننا تعديل السلاسل النصية في دوالنا للإشارة إلى اسم الصنف، على النحو التالي: class Shark: def __init__(self, name): self.name = name def swim(self): # الإشارة إلى الاسم print(self.name + " is swimming.") def be_awesome(self): # الإشارة إلى الاسم print(self.name + " is being awesome.") أخيرًا، يمكننا تعيين اسم الكائن ‎sammy‎ عند القيمة ‎"Sammy"‎ (أي قيمة الخاصية name) بتمريره إلى ‎Shark()‎ عند إنشائه: class Shark: def __init__(self, name): self.name = name def swim(self): print(self.name + " is swimming.") def be_awesome(self): print(self.name + " is being awesome.") def main(): # Shark تعيين اسم كائن sammy = Shark("Sammy") sammy.swim() sammy.be_awesome() if __name__ == "__main__": main() عرّفنا التابع ‎__init__‎، والذي يقبل مُعاملين ‎self‎ و name (تذكر أن المعامل ‎self‎ يُمرر تلقائيا إلى التابع)، ثم عرّفنا متغيرًا فيه. عند تنفيذ البرنامج: python shark.py سنحصل على: Sammy is swimming. Sammy is being awesome. لقد طُبع الاسم الذي مرّرناه إلى الكائن. ونظرًا لأنّ الباني يُنفّذ تلقائيًا، فلست بحاجة إلى استدعائه بشكل صريح، فيكفي تمرير الوسائط بين القوسين التاليين لاسم الصنف عند إنشاء نسخة جديدة منه. إذا أردت إضافة معامل آخر، مثل ‎age‎، فيمكن ذلك عبر تمريره إلى التابع ‎__init__‎: class Shark: def __init__(self, name, age): self.name = name self.age = age عند إنشاء الكائن ‎sammy‎، سنمرر عُمره أيضًا بالإضافة إلى اسمه: sammy = Shark("Sammy", 5) إذًا، تتيح البانيات تهيئة خاصيات الكائن لحظة إنشائه. العمل مع عدة كائنات تتيح لنا الأصناف إنشاء العديد من الكائنات المتماثلة التي تتبع نفس النمط. لتفهم ذلك بشكل أفضل، دعنا نضيف كائنًا آخر من الصنف ‎Shark‎ إلى برنامجنا: class Shark: def __init__(self, name): self.name = name def swim(self): print(self.name + " is swimming.") def be_awesome(self): print(self.name + " is being awesome.") def main(): sammy = Shark("Sammy") sammy.be_awesome() stevie = Shark("Stevie") stevie.swim() if __name__ == "__main__": main() لقد أنشأنا كائنًا ثانيًا من الصنف ‎Shark‎ يسمى ‎stevie‎، ومرّرنا إليه الاسم ‎"Stevie"‎. في هذا المثال، استدعينا التابع ‎be_awesome()‎ مع الكائن ‎sammy‎، والتابع ‎swim()‎ مع الكائن ‎stevie‎. لننفذ البرنامج: python shark.py سنحصل على المخرجات التالية: Sammy is being awesome. Stevie is swimming. يبدو ظاهرًا في المخرجات أننا نستخدم كائنين مختلفين، الكائن ‎sammy‎ والكائن ‎stevie‎، وكلاهما من الصنف ‎Shark‎. تتيح لنا الأصناف إنشاء عدة كائنات تتبع كلها نفس النمط دون الحاجة إلى بناء كل واحد منها من البداية. خلاصة تطرَّقنا في هذا الدرس إلى عِدَّة مفاهيم، مثل إنشاء الأصناف، وإنشاء الكائنات، وتهيئة الخاصيات باستخدام البانيات، والعمل مع أكثر من كائن من نفس الصنف. تُعدُّ البرمجة الكائنية أحد المفاهيم الضرورية التي ينبغي أن يتعلمها كل مبرمجي بايثون، لأنها تساعد على كتابة شيفرات قابلة لإعادة الاستخدام، إذ أنَّ الكائنات التي تُنشَأ في برنامج ما يمكن استخدامها في برامج أخرى. كما أنّ البرامج الكائنية عادة ما تكون أوضح وأكثر مقروئية، خصوصًا في البرامج المعقدة التي تتطلب تخطيطًا دقيقًا، وهذا بدوره يسهل صيانة البرامج مستقبلًا. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة -وبتصرّف- للمقال How To Construct Classes and Define Objects in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة التالية: فهم متغيرات الأصناف والنسخ في بايثون 3 المقالة السابقة: كيفية استخدام ‎args و ‎*kwargs في بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  12. طُوِّرَت بايثون في أواخر الثمانينات، وأُطلقَت لأول مرة عام 1991. استُوحي اسمها من المجموعة الكوميدية البريطانية Monty Python، تُعدُّ بايثون خليفة للغة البرمجة ABC متعددة الأغراض. تضمنت بايثون في إصداراتها الأولى معالجة الاستثناءات، والدوال، والأصناف والوراثة. table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } سيرشدك هذا الدرس إلى أفضل آليات وممارسات ترحيل الشيفرات من بايثون 2 إلى بايثون 3، وما إن كان عليك جعل الشيفرة متوافقة مع كلا الإصدارين. خلفية عامة صدر الإصدار 2 من بايثون عام 2000، ليُدشِّن حقبةً جديدةً من التطوير تقوم على الشفافية والشمولية، إذ شمل هذا الإصدار العديد من الميزات البرمجية، واستمر في إضافة المزيد طوال مدة تطويره. يُعد إصدار بايثون 3 مستقبل بايثون، وهو إصدار اللغة قيد التطوير حاليًا، فجاء في أواخر عام 2008، ليعالج العديد من عيوب التصميم الداخلية ويُعدِّلها. بيْد أنَّ اعتماد بايثون 3 كان بطيئًا بسبب عدم توافقه مع بايثون 2. في خضم ذلك، جاء الإصدار بايثون 2.7 في عام 2010 ليكون آخر إصدارات بايثون ‎2.x وليُسِّهل على مستخدمي بايثون ‎2.x الانتقال إلى بايثون 3 من خلال توفير قدر من التوافق بين الاثنتين، فهذا هو الهدف الأساسي من إطلاقه. يمكنك معرفة المزيد حول إصدارات بايثون والاختيار من بينها من المقالة: اعتبارات عملية للاختيار ما بين بايثون 2 و بايثون 3. ابدأ ببايثون 2.7 للانتقال إلى بايثون 3، أو لدعم بايثون 2 وبايثون 3 معًا، يجب عليك التأكد من أنّ شيفرة بايثون 2 متوافقة تمامًا مع بايثون 2.7. يعمل العديد من المطورين حصريًا بشيفرات بايثون 2.7، أمَّا المبرمجون الذي يعملون بشيفرات أقدم، فعليهم أن يتأكدوا من أنّ الشيفرة تعمل جيدًا مع بايثون 2.7، وتتوافق معه. التأكُّد من توافق الشيفرة مع بايثون 2.7 أمرٌ بالغ الأهمية لأنه الإصدار الوحيد من بايثون 2 الذي ما يزال قيد الصيانة، وتُصحَّحُ ثغراته. فإذا كنت تعمل بإصدار سابق من بايثون 2، فستجد نفسك تتعامل مع مشكلات في شيفرة لم تعد مدعومة، ولم تعد ثغراتها تُصحَّح. هناك أيضًا بعض الأدوات التي تسِّهل ترحيل الشيفرة، مثل الحزمة Pylint التي تبحث عن الأخطاء البرمجية، لكن لا تدعمها إصدارات بايثون السابقة للإصدار 2.7. من المهم أن تضع في حسبانك أنَّه رغم أنَّ بايثون 2.7 ما زالت قيد الدعم والصيانة في الوقت الحالي، إلا أنَّها ستموت في النهاية. ستجد في PEP 373 تفاصيل الجدول الزمني لإصدار بايثون 2.7، وفي وقت كتابة هذا المقال، فإنّ أجل بايثون 2.7 حُدِّد في عام 2020 (يحتمل أن تكون قد ماتت وأنت تقرأ هذه السطور :-| ). الاختبار اختبار الشيفرة جزءٌ أساسيٌّ من عملية ترحيل شيفرة بايثون 2 إلى بايثون 3. فإذا كنت تعمل على أكثر من إصدار واحد من بايثون، فعليك أيضًا التحقُّق من أنَّ أدوات الاختبار التي تستخدمها تغطي جميع الإصدارات للتأكُّد من أنَّها تعمل كما هو متوقع. يمكنك إضافة حالات بايثون التفاعلية (interactive Python cases) إلى السلاسل النصية التوثيقية (docstrings) الخاصة بكافة الدوال والتوابع والأصناف والوحدات، ثم استخدام الوحدة doctest المضمنة للتحقق من أنها تعمل كما هو موضح، إذ يعدُّ ذلك جزءًا من عملية الاختبار. إلى جانب doctest، يمكنك استخدام الحزمة package.py لتتبع وحدة الاختبار. ستراقب هذه الأداة برنامجك وتحدد الأجزاء التي تُنفِّذها من الشيفرة، والأجزاء التي يمكن تنفيذها ولكن لم تُنفَّذ. يمكن أن تطبع Cover.py تقارير في سطر الأوامر، أو تنتج مستند HTML. تُستخدم عادةً لقياس فعالية الاختبارات، إذ توضح الأجزاء من الشيفرة التي اختُبِرت، والأجزاء التي لم تُختبَر. تَذكَّر أنه ليس عليك اختبار كل شيء، لكن تأكَّد من تغطية أيّ شيفرة غامضة أو غير عادية. للحصول على أفضل النتائج، يُنصح أن تشمل التغطية 80٪ من الشيفرة. تعرف على الاختلافات بين بايثون 2 و بايثون 3 سيمكّنك التعرّف على الاختلافات بين بايثون 2 و بايثون 3 من استخدام الميزات الجديدة المتاحة، أو التي ستكون متاحة في بايثون 3. تتطرق مقالتنا حول "اعتبارات عملية للاختيار ما بين بايثون 2 و بايثون 3" إلى بعض الاختلافات الرئيسية بين الإصدارين. يمكنك أيضًا مراجعة توثيق بايثون الرسمي لمزيد من التفاصيل. عند البدء في ترحيل الشيفرة، فهناك بعض التغييرات في الصياغة عليك تنفيذها فوريًا. print حلت الدالة print()‎‎ في بايثون 3 مكان التعليمة print في بايثون 2: بايثون 2 بايثون 3 "مرحبا بالعالم!" print ("مرحبا بالعالم!")print exec تغيَّرت التعليمة exec في بايثون 2 وأصبحت دالةً تسمح بمتغيرات محلية (locals) وعامة (globals) صريحة في بايثون 3: بايثون 2 بايثون 3 exec code exec(code)‎ exec code in globals exec(code, globals)‎ exec code in (globals, locals)‎ exec(code, globals, locals)‎ / و // تُجرِي بايثون 2 القسمة التقريبية (floor division) بالعامل / ، بينما تخصص بايثون 3 العامل // لإجراء القسمة التقريبية: بايثون 2 بايثون 3 ‎5 / 2 = 2‎ ‎5 / 2 = 2.5‎ ‎5 // 2 = 2‎ لاستخدام هذين المعاملين في بايثون 2، استورد division من الوحدة __future__: from __future__ import division اقرأ المزيد عن قسمة الأعداد الصحيحة من المقالة: اعتبارات عملية للاختيار ما بين بايثون 2 و بايثون 3. raise في بايثون 3، يتطلب إطلاق الاستثناءات ذات الوسائط استخدام الأقواس، كما لا يمكن استخدام السلاسل النصية كاستثناءات: بايثون 2 بايثون 3 raise Exception, args raise Exception raise Exception(args)‎ raise Exception, args, traceback raise Exception(args).with_traceback(traceback)‎ raise "Error"‎ raise Exception("Error")‎ except في بايثون 2، كان من الصعب إدراج الاستثناءات المُتعدِّدة، لكن ذلك تغيَّر في بايثون 3. لاحظ أنَّ as تُستخدَم صراحةً مع except في بايثون 3: بايثون 2 بايثون 3 except Exception, variable:‎ except AnException as variable:‎ except (OneException, TwoException) as variable:‎ def في بايثون 2، يمكن للدوال أن تقبل سلاسل مثل الصفوف أو القوائم. أمَّا في بايثون 3، فقد أزيل هذا الأمر. بايثون 2 بايثون 3 def function(arg1, (x, y)):‎ def function(arg1, x_y): x, y = x_y expr لم تعد صياغة علامة الاقتباس المائلة `` في بايثون 2 صالحة، واستخدم بدلًا عنها repr()‎أوstr.format()‎` في بايثون 3. بايثون 2 بايثون 3 `x = `355/113`` `x = repr(355/113):` تنسيق السلاسل النصية (String Formatting) لقد تغيرت صياغة تنسيق السلاسل النصية من بايثون 2 إلى بايثون 3. بايثون 2 بايثون 3 `"%d %s" % (i, s)` `"{} {}".format(i, s)` `"%d/%d=%f" % (355, 113, 355/113)` `"{:d}/{:d}={:f}".format(355, 113, 355/113)` تعلم كيفية استخدام تنسيقات السلاسل النصية في بايثون 3 من مقالة كيفية استخدام آلية تنسيق السلاسل النصية في بايثون 3. class ليست هناك حاجة لتمرير object في بايثون 3. بايثون 2 class MyClass(object): pass بايثون 3 class MyClass: pass في بايثون 3، تُضبَط الأصناف العليا (metaclasses) بالكلمة مفتاحية metaclass. بايثون 2: class MyClass: __metaclass__ = MyMeta class MyClass(MyBase): __metaclass__ = MyMeta بايثون 3: class MyClass(metaclass=type): pass class MyClass(MyBase, metaclass=MyMeta): pass تحديث الشيفرة هناك أدَاتان رئيّسيتان لتَحديث الشيفرة تلقائيًا إلى بايثون 3 مع الحفاظ على توافقيّتها مع بايثون 2 وهما: future و modernize. تختلف آليَتا عمل هاتين الأداتين، إذ تحاول future نقل أفضل ممارسات بايثون 3 إلى بايثون 2، في حين أنّ modernize تسعى إلى إنشاء شيفرات موحدة لبايثون تتوافق مع 2 و 3 وتستخدم الوحدة six لتحسين التوافقية. يمكن أن تساعدك هاتان الأداتان في إعادة كتابة الشيفرة وتحديد ورصد المشاكل المحتملة وتصحيحها. يمكنك تشغيل الأداة عبر مجموعة unittest لفحص الشيفرة والتحقق منها بصريًا، والتأكد من أنّ المراجعات التلقائية التي أُجريَت دقيقة. وبمجرد انتهاء الاختبارات، يمكنك تحويل الشيفرة. بعد هذا، ستحتاج على الأرجح إلى إجراء مراجعة يدوية، وخاصة استهداف الاختلافات بين بايثون 2 و 3 المذكورة في القسم أعلاه. إن أردت استخدام future، فعليك إضافة عبارة الاستيراد التالية في جميع وحدات بايثون 2.7: from __future__ import print_function, division, absolute_imports, unicode_literals رغم أن هذا لن يعفيك من إعادة كتابة الشيفرة، إلا أنه سيضمن لك أن تتماشى شيفرة بايثون 2 مع صياغة بايثون 3. أخيرًا، يمكنك استخدام الحزمة pylint لتحديد ورصد أي مشكلات محتملة أخرى في الشيفرة. تحتوي هذه الحزمة على مئات القواعد التي تغطي مجموعة واسعة من المشكلات التي قد تطرأ، بما فيها قواعد الدليل PEP 8، بالإضافة إلى أخطاء الاستخدام. قد تجد أنّ بعض أجزاء شيفرتك تربك pylint وأدوات الترحيل التلقائي الأخرى. حاول تبسيطها، أو استخدم unittest. التكامل المستمر (Continuous Integration) إذا أردت أن تجعل شفرتك متوافقة مع عدة إصدارات من بايثون، فستحتاج إلى تشغيل الإطار unittest باستمرار وفق مبدأ التكامل المستمر (وليس يدويًا)، أي أن تفعل ذلك أكبر عدد ممكن من المرات أثناء عملية التطوير. إذا كنت تستخدم الحزمة six لصيانة التوافقية بين بايثون 2 و 3، فستحتاج إلى استخدام عدة بيئات عمل لأجل الاختبار. إحدى حزم إدارة البيئة التي قد تكون مفيدة لك هي الحزمة tox، إذ ستفحص تثبيتات الحزمة مع مختلف إصدارات بايثون، وإجراء الاختبارات في كل بيئة من بيئات عملك، كما يمكن أن تكون بمثابة واجهة عمل للتكامل المستمر. خلاصة من المهم أن تعلم أنه مع ازدياد تركيز المطورين على بايثون 3، فستصبح اللغة أكثر دقةً وتماشيًا مع احتياجات المبرمجين، وسيضعف دعم بايثون 2.7. إذا قرَّرت أن تجعل شيفرتك متوافقة مع كل من بايثون 2 و بايثون 3، فقد تواجه صعوبة في ذلك لأنَّ بايثون 2 ستتلقى دعمًا أقل مع مرور الوقت. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة -وبتصرّف- للمقال How To Port Python 2 Code to Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة السابقة: كيف تستخدم التسجيل Logging في بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون تعرف على أبرز مميزات لغة بايثون
  13. تأتي وحدة التسجيل (logging) مبدئيا توزيعة بايثون المعيارية وتقدم حلا لمتابعة الأحداث التي تحصُل أثناء عمل البرمجية. تستطيع إضافة استدعاءات التسجيل للشيفرة البرمجية الخاصة بك للإشارة لما تحقق من أحداث. تسمح لك وحدة التسجيل بإعداد كل من التسجيلات التشخيصية التي تسجل الأحداث المقترنة بعمليات التطبيق، بالإضافة إلى تسجيلات التدقيق التي تسجل الأحداث الخاصة بحركات المستخدم بهدف تحليلها. وحدة التسجيل هذه مختصة في حفظ السجلات في ملفات. لماذا نستخدم وحدة التسجيل؟ تحافظ وحدة التسجيل على سجل الأحداث التي تحدث خلال عمل البرنامج، مما يجعل من رؤية مخرجات البرنامج المتعلقة بأحداثه أمراً متاحاً. قد يكون استخدام الأمرprint أمرا مألوفا لك خلال الشيفرة البرمجية لفحص الأحداث. يقدم الأمر print طريقة بدائية لإجراء عملية التنقيح الخاصة بحل المشاكل خلال عمل البرمجية. بينما يعد تضمين تعليمات print خلال الشيفرة البرمجية طريقة لمعرفة مسار تنفيذ البرنامج والحالة الحالية له، إلا أن هذه الطريقة اُثبت أنها أقل قدرة على الصيانة من استخدام وحدة التسجيل في بايثون، وذلك للأسباب التالية: باستخدام تعليمات print يصبح من الصعب التفرقة بين مخرجات البرنامج الطبيعية وبين مخرجات التنقيح لتشابههما. عندما تنتشر تعليمات print خلال الشيفرة البرمجية، فإنه لا توجد طريقة سهلة لتعطيل التعليمات الخاصة بالتنقيح. من الصعب إزالة كافة تعليمات print عندما تنتهي من عملية التنقيح. لا توجد سجلات تشخيصية للأحداث. من الجيد البدء بالتعود على استخدام وحدة التسجيل المعيارية في بايثون خلال كتابة الشيفرة البرمجية بما أنها طريقة تتلاءم أكثر مع التطبيقات التي يكبر حجمها عن سكريبتات بايثون بسيطة، وكذلك بما أنها تقدم طريقة أفضل للتنقيح. طباعة رسائل التنقيح في وحدة التحكم إذا كنت متعودا على استخدام تعليمات print لرؤية ما يحدث في برنامجك خلال العمل، فمن المحتمل مثلا أنك تعودت على رؤية برنامج يُعرف صنفًاClass وينشئ منه عناصر كما في المثال التالي: class Pizza(): def __init__(self, name, price): self.name = name self.price = price print("Pizza created: {} (${})".format(self.name, self.price)) def make(self, quantity=1): print("Made {} {} pizza(s)".format(quantity, self.name)) def eat(self, quantity=1): print("Ate {} pizza(s)".format(quantity, self.name)) pizza_01 = Pizza("artichoke", 15) pizza_01.make() pizza_01.eat() pizza_02 = Pizza("margherita", 12) pizza_02.make(2) pizza_02.eat() توجد في الشيفرة السابقة الدالة__init__ التي تستخدم لتعريف خصائصname وprice للصنفPizza. كما تحتوي على الدالتين make لصنع البيتزا، وeat لأكلها وتأخذان المعطى quantity ذا القيمة الافتراضية 1. لنشغل البرنامج: >> python pizza.py وسنحصل على المخرج التالي: Output Pizza created: artichoke ($15) Made 1 artichoke pizza(s) Ate 1 pizza(s) Pizza created: margherita ($12) Made 2 margherita pizza(s) Ate 1 pizza(s) تسمح لنا تعليمات print برؤية أن البرنامج يعمل، ولكننا نستطيع أن نستخدم وحدة التسجيل لذات الغرض بدلا من ذلك. لنقم بإزالة تعليمات print من الشيفرة البرمجية، ونستورد الوحدة باستخدام الأمر import logging: import logging class Pizza(): def __init__(self, name, value): self.name = name self.value = value ... المستوى التلقائي للتسجيل في وحدة التسجيل هو مستوى التحذير (WARNING)، وهو مستوى فوق مستوى التنقيح (DEBUG). بما أننا سنستخدم الوحدة بغرض التنقيح في هذا المثال، سنحتاج الى تعديل إعدادات التسجيل لتصبح بمستوى التنقيح logging.DEBUG بحيث تعود معلومات التنقيح لنا من خلال لوحة التحكم. ونقوم بإعداد ذلك بإضافة ما يلي بعد تعليماتة الاستيراد: import logging logging.basicConfig(level=logging.DEBUG) class Pizza(): ... هذا المستوى المتمثل بlogging.DEBUG يشير لقيد رقمي قيمته 10. سنستبدل الآن جميع تعليمات print بتعليمات logging.debug()، (logging.DEBUG ثابت بينما logging.debug() دالة). نستطيع أن نمرر لهذه الدالة نفس المدخلات النصية لتعليماتة print كما هو موجود بالأسفل: import logging logging.basicConfig(level=logging.DEBUG) class Pizza(): def __init__(self, name, price): self.name = name self.price = price logging.debug("Pizza created: {} (${})".format(self.name, self.price)) def make(self, quantity=1): logging.debug("Made {} {} pizza(s)".format(quantity, self.name)) def eat(self, quantity=1): logging.debug("Ate {} pizza(s)".format(quantity, self.name)) pizza_01 = Pizza("artichoke", 15) pizza_01.make() pizza_01.eat() pizza_02 = Pizza("margherita", 12) pizza_02.make(2) pizza_02.eat() لهذا الحد، نستطيع تشغيل البرنامج عبر تنفيذ الأمر python pizza.py وسنحصل على المخرج التالي: Output DEBUG:root:Pizza created: artichoke ($15) DEBUG:root:Made 1 artichoke pizza(s) DEBUG:root:Ate 1 pizza(s) DEBUG:root:Pizza created: margherita ($12) DEBUG:root:Made 2 margherita pizza(s) DEBUG:root:Ate 1 pizza(s) لاحظ أن مستوى التسجيل في المخرج السابق هو DEBUG بالإضافة لكلمة root والتي تشير لمستوى المُسجل (logger) الذي يتم استخدامه. يعني ما سبق أن وحدة التسجيل logging من الممكن أن يتم استخدامها لإعداد أكثر من مُسجل بأسماء مختلفة. فمثلا، نستطيع إنشاء مسجلين باسمين مختلفين ومخرجات مختلفة كما هو موضح بالأسفل: logger1 = logging.getLogger("module_1") logger2 = logging.getLogger("module_2") logger1.debug("Module 1 debugger") logger2.debug("Module 2 debugger") Output DEBUG:module_1:Module 1 debugger DEBUG:module_2:Module 2 debugger بعد أن أصبحت لدينا المعرفة اللازمة لكيفية استخدام الوحدة logging لطباعة الرسائل على وحدة التحكم، دعونا نكمل شرح الوحدة ونتعرف على كيفية استخدام الوحدة في طباعة الرسائل إلى ملف خارجي. التسجيل في ملف الغرض الأساسي للتسجيل هو حفظ البيانات في ملف وليس إظهار معلومات التسجيل على وحدة التحكم. يتيح لك التسجيل في ملف حفظ بيانات التسجيل مع مرور الوقت واستخدامها في عملية التحليل والمتابعة ولتحديد ما تحتاجه من تغيير على الشيفرة البرمجية. لجعل عملية التسجيل تحفظ التسجيلات في ملف، علينا أن نعدّل logging.basicConfig() بحيث تحتوي على معطى لاسم الملف (filename)، وليكن مثلا test.log: import logging logging.basicConfig(filename="test.log", level=logging.DEBUG) class Pizza(): def __init__(self, name, price): self.name = name self.price = price logging.debug("Pizza created: {} (${})".format(self.name, self.price)) def make(self, quantity=1): logging.debug("Made {} {} pizza(s)".format(quantity, self.name)) def eat(self, quantity=1): logging.debug("Ate {} pizza(s)".format(quantity, self.name)) pizza_01 = Pizza("artichoke", 15) pizza_01.make() pizza_01.eat() pizza_02 = Pizza("margherita", 12) pizza_02.make(2) pizza_02.eat() الشيفرة البرمجية هنا هي نفسها الموجودة سابقا عدا أننا أضفنا اسم الملف الذي سنقوم بحفظ التسجيلات فيه. بمجرد تشغيلنا للشيفرة السابقة، سنجد في نفس المسار الملف test.log. لنفتحه باستخدام محرر النصوص nano (أو أي محرر نصوص من اختيارك): $ nano test.log وسيكون محتويات الملف كالتالي: DEBUG:root:Pizza created: artichoke ($15) DEBUG:root:Made 1 artichoke pizza(s) DEBUG:root:Ate 1 pizza(s) DEBUG:root:Pizza created: margherita ($12) DEBUG:root:Made 2 margherita pizza(s) DEBUG:root:Ate 1 pizza(s) المخرج السابق هو نفسه الذي حصلنا عليه في القسم السابق من المقال، غير أنه الآن في ملف باسم test.log وليس على الطرفية. لنغلق المحرر، ونجر بعض التعديلات التالية على المتغيرين pizza_01 و pizza_02: ... # Modify the parameters of the pizza_01 object pizza_01 = Pizza("Sicilian", 18) pizza_01.make(5) pizza_01.eat(4) # Modify the parameters of the pizza_02 object pizza_02 = Pizza("quattro formaggi", 16) pizza_02.make(2) pizza_02.eat(2) عند تنفيذ الشيفرة بعد حفظ التعديلات، ستُضاف التسجيلات الجديدة للملف وسيكون محتواه كالتالي: DEBUG:root:Pizza created: artichoke ($15) DEBUG:root:Made 1 artichoke pizza(s) DEBUG:root:Ate 1 pizza(s) DEBUG:root:Pizza created: margherita ($12) DEBUG:root:Made 2 margherita pizza(s) DEBUG:root:Ate 1 pizza(s) DEBUG:root:Pizza created: Sicilian ($18) DEBUG:root:Made 5 Sicilian pizza(s) DEBUG:root:Ate 4 pizza(s) DEBUG:root:Pizza created: quattro formaggi ($16) DEBUG:root:Made 2 quattro formaggi pizza(s) DEBUG:root:Ate 2 pizza(s) تُعد البيانات الموجودة في الملف مفيدة، ولكننا نستطيع جعلها أكثر إعلاماً بإضافة بعض الإعدادات. بشكل أساسي، فإننا نريد أن نجعل السجلات مفصلة أكثر بإضافة الوقت الذي أنشئ السجل فيه. نستطيع إضافة المعطى المسمى format ونضيف له النص %(asctime)s الذي يشير للوقت، كذلك، للإبقاء على ظهور مستوى التسجيل في السجلات، لابد أن نضيف النص %(levelname)s بالإضافة للسجل نفسه %(message)s. لابد من الفصل بين كل خيار في المعطى format بالعلامة : كما هو موضح بالأسفل: import logging logging.basicConfig( filename="test.log", level=logging.DEBUG, format="%(asctime)s:%(levelname)s:%(message)s" ) ....... عندما ننفذ الشيفرة السابقة، سنحصل على تسجيلات جديدة في ملف test.log تتضمن الوقت الذي أنشئ فيه التسجيل بالإضافة لمستوى التسجيل ورسالة التسجيل: Output DEBUG:root:Pizza created: Sicilian ($18) DEBUG:root:Made 5 Sicilian pizza(s) DEBUG:root:Ate 4 pizza(s) DEBUG:root:Pizza created: quattro formaggi ($16) DEBUG:root:Made 2 quattro formaggi pizza(s) DEBUG:root:Ate 2 pizza(s) 2017-05-01 16:28:54,593:DEBUG:Pizza created: Sicilian ($18) 2017-05-01 16:28:54,593:DEBUG:Made 5 Sicilian pizza(s) 2017-05-01 16:28:54,593:DEBUG:Ate 4 pizza(s) 2017-05-01 16:28:54,593:DEBUG:Pizza created: quattro formaggi ($16) 2017-05-01 16:28:54,593:DEBUG:Made 2 quattro formaggi pizza(s) 2017-05-01 16:28:54,593:DEBUG:Ate 2 pizza(s) تبعا لاحتياجاتك، من الممكن أن تضيف إعدادات أخرى للمسجل بحيث تجعل التسجيلات التي يتم حفظها في الملف مرتبطة بك نوعا ما. التنقيح بواسطة التسجيل في ملفات خارجية يتيح لك فهما شاملا لبرنامج بايثون مع مرور الوقت، معطيا الفرصة لحل المشاكل التي تظهر وتغيير ما تحتاج تغييره في الشيفرة البرمجية استنادا لما لديك من بيانات تسجيلات تاريخية وأحداث وحركات تمت خلال عمل البرنامج. جدول بمستويات التسجيل تستطيع نسب مستوى الأهمية للحدث الذي يتم تسجيله بواسطة المُسجل وذلك بإضافة مستوى الخطورة (Severity Level). مستويات الخطورة موضحة في الجدول الذي بالأسفل. تتمثل مستويات التسجيل تقنيا بأرقام (ثوابت)، بفرق قيمة بين كل مستوى ب 10، تبدأ من المستوىNOTEST ذي القيمة 0. تستطيع أن تُعرف مستويات خاصة بك مرتبطة بالمستويات المعرفة مسبقا. إذا عَرَّفْتَ مستوى بنفس القيمة الرقمية، فإنك تستبدل اسم المستوى المرتبط بتلك القيمة. المستوى القيمة الرقمية الدالة الاستخدام CRITICAL 50 ()logging.critical اظهار الأخطاء الخطيرة، البرنامج قد لا يستمر بالعمل ERROR 40 ()logging.error إظهار مشكلة خطيرة WARNING 30 ()logging.warning الإشارة لحدث غير متوقع حصل أو قد يصحل INFO 20 ()logging.info الإشارة أن الحدث حصل كما هو متوقع DEBUG 10 ()logging.debug فحص المشاكل، وإظهار معلومات تفصيلية خاتمة الوحدة logging هي وحدة ضمن التوزيعة المعيارية لبايثون، وتقدم حلا لمتابعة الأحداث التي تحدث خلال عمل البرمجية مع إمكانية تسجيل هذه الأحداث في ملفات خارجية أو إظهارها على الطرفية. وهذا يتيح لك فهما شاملا لبرنامج بايثون مع مرور الوقت. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة – بتصرّف – للمقال How To Use Logging in Python 3 لصاحبته Lisa Tagliaferri. اقرأ أيضًا المقالة التالية: كيفية ترحيل شيفرة بايثون 2 إلى بايثون 3 المقالة السابقة: كيفية تنقيح شيفرات بايثون من سطر الأوامر التفاعلي المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  14. التنقيح (debugging) هو جزء من عملية تطوير البرمجيات، ويروم البحث عن الأخطاء والمشاكل في الشيفرة، والتي تحول دون تنفيذ البرنامج تنفيذًا صحيحًا. الوحدة code هي إحدى الأدوات المفيدة التي يمكن استخدامها لمحاكاة المترجم (interpreter) التفاعلي، إذ توفر هذه الوحدة فرصةً لتجربة الشيفرة التي تكتبها. قبل أن تكمل هذا المقال، أنصحك بمطالعة المقال السابق عن منقح بايثون: كيف تستخدم منقح بايثون. فهم الوحدة code بدلاً من تفحص الشيفرة باستخدام منقح، يمكنك إضافة الوحدة code لوضع نقاط لإيقاف تنفيذ البرنامج، والدخول في الوضع التفاعلي لتفحص ومتابعة كيفية عمل الشيفرة. الوحدة code هي جزء من مكتبة بايثون القياسية. هذه الوحدة مفيدةٌ لأنَّها ستمكنك من استخدام مترجم دون التضحية بالتعقيد والاستدامة التي توفرها ملفات البرمجة. فيمكنك عبر استخدام الوحدة code تجنب استخدام الدالة print()‎‎‎ في شيفرتك لأجل التنقيح، لأنها طريقة غير عملية. لاستخدام هذه الوحدة في تنقيح الأخطاء، يمكنك استخدام الدالة interact()‎ الخاصة بالوحدة code، والتي توقف تنفيذ البرنامج عند استدعائها، وتوفر لك سطر أوامر تفاعلي حتى تتمكن من فحص الوضع الحالي لبرنامجك. تُكتَب هذه الدالة هكذا: code.interact(banner=None, readfunc=None, local=None, exitmsg=None) تُنفِّذ هذه الدالة حلقة اقرأ-قيِّم-اطبع (تختصر إلى REPL، أي Read–eval–print loop، وتنشئ نسخة من الصنف InteractiveConsole، والذي يحاكي سلوك مترجم بايثون التفاعلي. هذه هي المعاملات الاختيارية: banner: يمكن أن تعطيه سلسلة نصية لتعيين موضع إطلاق المترجم. readfunc: يمكن استخدامه مثل التابع InteractiveConsole.raw_input‎()‎. local: سيعينّ فضاء الأسماء (namespace) الافتراضي لحلقة المترجم (interpreter loop). exitmsg: يمكن إعطاؤه سلسلة نصية لتعيين موضع توقف المترجم. مثلًا، يمكن استخدام المعامل local بهذا الشكل: local=locals()‎ لفضاء أسماء محلي. local=globals()‎ لفضاء أسماء عام. local=dict(globals(), **locals())‎ لاستخدام كل من فضاء الأسماء العام، وفضاء الأسماء المحلي الحالي. المعامل exitmsg جديد، ولم يظهر حتى إصدار بايثون 3.6، لذلك إن كنت تستخدم إصدارًا أقدم، فحدّثه، أو لا تستخدم المعامل exitmsg. ضع الدالة interact()‎‎ حيث تريد إطلاق المترجم التفاعلي في الشيفرة. كيفية استخدام الوحدة code لتوضيح كيفية استخدام الوحدة code، سنكتب بُريمجًا عن الحسابات المصرفية يسمى balances.py. سنعيّن المعامل المحلي عند القيمة locals()‎‎‎ لجعل فضاء الأسماء محليًّا. # `code` استيراد الوحدة import code bal_a = 2324 bal_b = 0 bal_c = 409 bal_d = -2 account_balances = [bal_a, bal_b, bal_c, bal_d] def display_bal(): for balance in account_balances: if balance < 0: print("Account balance of {} is below 0; add funds now." .format(balance)) elif balance == 0: print("Account balance of {} is equal to 0; add funds soon." .format(balance)) else: print("Account balance of {} is above 0.".format(balance)) # لبدء المترجم بفضاء أسماء محلي interact()‎‎‎ استخدام code.interact(local=locals()) display_bal() لقد استدعينا الدالة code.interact()‎‎‎ مع المعامل local=locals()‎ لاستخدام فضاء الأسماء المحلي كقيمة افتراضية داخل حلقة المترجم. لنُنفِّذ البرنامج أعلاه باستخدام الأمر python3 إذا لم نكن تعمل في بيئة افتراضية، أو الأمر python خلاف ذلك: python balances.py بمجرد تنفيذ البرنامج، سنحصل على المخرجات التالية: Python 3.5.2 (default, Nov 17 2016, 17:05:23) [GCC 5.4.0 20160609] on linux Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> سيتم وضع المؤشر في نهاية السطر ‎>>>‎، كما لو أنك في سطر الأوامر التفاعلي. من هنا، يمكنك استدعاء الدالة print()‎ لطباعة المتغيرات والدوال وغير ذلك: >>> print(bal_c) 409 >>> print(account_balances) [2324, 0, 409, -2] >>> print(display_bal()) Account balance of 2324 is 0 or above. Account balance of 0 is equal to 0, add funds soon. Account balance of 409 is 0 or above. Account balance of -2 is below 0, add funds now. None >>> print(display_bal) <function display_bal at 0x104b80f28> >>> نرى أنه باستخدام فضاء الأسماء المحلي، يمكننا طباعة المتغيرات، واستدعاء الدالة. يُظهر الاستدعاء الأخير للدالة print()‎ أنّ الدالة display_bal موجودة في ذاكرة الحاسوب. بعد أن تنتهي من العمل على المترجم، يمكنك الضغط على CTRL + D في الأنظمة المستندة إلى *نكس، أو CTRL + Z في أنظمة ويندوز لمغادرة سطر الأوامر ومتابعة تنفيذ البرنامج. إذا أردت الخروج من سطر الأوامر دون تنفيذ الجزء المتبقي من البرنامج، فاكتب quit()‎‎‎، وسيتوقف البرنامج. في المثال التالي، سنستخدم المُعاملين banner و exitmsg: # لبدء المترجم interact()‎‎‎ استخدم الدالة code.interact(banner="Start", local=locals(), exitmsg="End") display_bal() عند تنفيذ البرنامج، ستحصل على المخرجات التالية: Start >>> يتيح لك استخدام المعامل banner تعيين عدة نقاط داخل شيفرتك، مع القدرة على تحديدها. على سبيل المثال، يمكن أن يكون لديك معامل banner يطبع السلسلة النصية "In for-loop" مع معامل exmsg يطبع "Out of for-loop"، وذلك حتى تعرف مكانك بالضبط في الشيفرة. من هنا، يمكننا استخدام المترجم كالمعتاد. بعد كتابة CTRL + D للخروج من المترجم، ستحصل على رسالة الخروج، وسيتم تنفيذ الدالة: End Account balance of 2324 is 0 or above. Account balance of 0 is equal to 0, add funds soon. Account balance of 409 is 0 or above. Account balance of -2 is below 0, add funds now. سيتم تنفيذ البرنامج بالكامل بعد الجلسة التفاعلية. بمجرد الانتهاء من استخدام الوحدة code لتنقيح الشيفرة، يجب عليك إزالة دوال الوحدة code وعبارة الاستيراد حتى يُنفَّذ البرنامج مثل المعتاد. خلاصة تُستخدَم الوحدة code لإطلاق سطر الأوامر التفاعلي لتفحُّص الشيفرة خطوةً بخطوة بقصد فهم سلوكها، وتعديل الشيفرة إن لزم الأمر. لقراءة المزيد حول هذا الموضوع، يمكنك مطالعة التوثيق الرسمي للوحدة code. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة -وبتصرّف- للمقال How To Debug Python with an Interactive Console لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة التالية: كيف تستخدم التسجيل Logging في بايثون 3 المقالة السابقة: كيف تستخدم منقح بايثون المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  15. نعني بالتنقيح Debugging في مجال تطوير البرمجيات، عملية إيجاد وحل المشاكل التي تمنع البرمجية من العمل على نحو سليم. يُقدم مُنقح بايثون بيئة تنقيح لبرامج بايثون، ويدعم إعداد نقاط الفصل (Breakpoints)، التدرج خلال الشيفرة البرمجية (Stepping) سطرًا بسطر والعديد من المزايا. التفاعل مع مُنقح بايثون يأتي مُنقح بايثون مرفقًا مع توزيعة بايثون المعيارية على هيئة وحدة بايثون باسم pdb، ومن الممكن أن يُستخدم في الشيفرة البرمجية كعنصر من الصنف Pdb، ولمزيد من المعلومات تستطيع قراءة التوثيق الرسمي للوحدة pdb. سوف نبدأ العمل باستخدام برنامج صغير يحتوي على متغيرين ودالة تحتوي على حلقة تكرار بالإضافة لسطر استدعاء الدالة من خلال التركيبة if __name__ == '__main__':. num_list = [500, 600, 700] alpha_list = ['x', 'y', 'z'] def nested_loop(): for number in num_list: print(number) for letter in alpha_list: print(letter) if __name__ == '__main__': nested_loop() نستطيع الآن تشغيل البرنامج السابق من خلال مُنقح بايثون في الطرفية باستخدام الأمر التالي: python -m pdb looping.py يستورد الخيار m- في الأمر أي وحدة بايثون وتشغيلها كبرنامج. في هذه الحالة نقوم باستيراد الوحدة pdb وتمريرها كما هو مكتوب في الأمر. عند تنفيذ الأمر السابق سوف تحصل على المُخرج التالي: > /Users/sammy/looping.py(1)<module>() -> num_list = [500, 600, 700] (Pdb) يحتوي السطر الأول في المُخرج على اسم الملف الحالي الذي نقوم بتنقيحه – بالمسار الكامل -، ومن ثم يوجد رقم سطر الشيفرة التي يقف عليها مؤشر المُنقح (في هذه الحالة 1، ولكن في حالة وجود تعليق أو سطر غير تنفيذي سيكون الرقم أكبر من ذلك). السطر الثاني عبارة عن سطر الشيفرة الذي يتم تنفيذه. يُقدم مُنقح بايثون وحدة تحكم (Console) تفاعلية لإجراء عملية التنقيح وتستطيع استخدام أمر المساعدةhelp للتعرف على الأوامر المتاحة في المُنقح، وأيضا من الممكن أن تستخدم صيغة المساعدة مع الأمرhelp command للتعرف أكثر على تفاصيل أمر معين. سيعيد المُنقح عمله مرة أخرى تلقائيا عندما يصل لنهاية البرنامج. إذا أردت الخروج من وحدة تحكم المُنقح، أدخل الأمرquit أوexit. إذا أردت إعادة تشغيل عملية التنقيح مرة أخرى وفي أي مكان من البرنامج، أدخل الأمرrun. استخدام المُنقح للتدرج خلال البرنامج عند العمل على تنقيح البرامج باستخدام مُنقح بايثون، فإنك غالبا ستستخدم أوامر step، list وnext للمرور على الشيفرة البرمجية. خلال هذا الجزء من المقال سنتناول هذه الأوامر. من خلال نافذة الأوامر، نستطيع إدخال الأمرlist للحصول على السياق المحيط للسطر الحالي. فمثلا، من السطر الأول في البرنامج looping.py الذي عرضناه في الأعلى — num_list = [500, 600, 700] — سيكون تنفيذ الأمرlist معه بالشكل التالي: (Pdb) list 1 -> num_list = [500, 600, 700] 2 alpha_list = ['x', 'y', 'z'] 3 4 5 def nested_loop(): 6 for number in num_list: 7 print(number) 8 for letter in alpha_list: 9 print(letter) 10 11 if __name__ == '__main__': (Pdb) السطر الحالي يشار إليه بالعلامة -> في بدايته وهو في حالتنا هذه السطر الأول من البرنامج. بحكم أن البرنامج الذي نستخدمه هنا صغير نسبيا، فإننا تقريبا نحصل على كافة الأسطر في البرنامج عند استخدامالأمر list. بدون تزويد معطيات مع الأمر list، نحصل من استخدام الأمر على 11 سطراً من الشيفرة البرمجية محيطة بالسطر الحالي في المُنقح، ولكننا نستطيع تحديد الأسطر التي نريد عرضها بالشكل التالي: (Pdb) list 3, 7 3 4 5 def nested_loop(): 6 for number in num_list: 7 print(number) (Pdb) في الأمر السابق قمنا بعرض الأسطر 3-7 باستخدام الأمر list 3,7. للمرور خلال البرنامج سطرا بسطر، نستخدم الأمرstep أو next. (Pdb) step > /Users/sammy/looping.py(2)<module>() -> alpha_list = ['x', 'y', 'z'] (Pdb) (Pdb) next > /Users/sammy/looping.py(2)<module>() -> alpha_list = ['x', 'y', 'z'] (Pdb) الفرق بين الأمرstep والأمرnext أن الأمرstep سوف يتوقف مع استدعاء دالة، بينما الأمر next يُنفذ الدالة عند استدعائها. يتجلى الفرق بين الأمرين عند التعامل مع الدوال. في مثالنا المستخدم، سيقوم الأمرstep بالتعداد خلال حلقات التكرار بمجرد الدخول في استدعاء الدالة nested_loop() بحيث يظهر تماما ما تقوم به حلقة التكرار من طباعة رقم ومن ثم الدخول في حلقة تكرار طباعة الحروف المقترنة بالرقم ومن ثم العودة لطباعة رقم جديد وهكذا. (Pdb) step > /Users/sammy/looping.py(5)<module>() -> def nested_loop(): (Pdb) step > /Users/sammy/looping.py(11)<module>() -> if __name__ == '__main__': (Pdb) step > /Users/sammy/looping.py(12)<module>() -> nested_loop() (Pdb) step --Call-- > /Users/sammy/looping.py(5)nested_loop() -> def nested_loop(): (Pdb) step > /Users/sammy/looping.py(6)nested_loop() -> for number in num_list: (Pdb) step > /Users/sammy/looping.py(7)nested_loop() -> print(number) (Pdb) step 500 > /Users/sammy/looping.py(8)nested_loop() -> for letter in alpha_list: (Pdb) step > /Users/sammy/looping.py(9)nested_loop() -> print(letter) (Pdb) step x > /Users/sammy/looping.py(8)nested_loop() -> for letter in alpha_list: (Pdb) step > /Users/sammy/looping.py(9)nested_loop() -> print(letter) (Pdb) step y > /Users/sammy/looping.py(8)nested_loop() -> for letter in alpha_list: (Pdb) الأمرnext، بدلا من ذلك، سيستدعي الدالة دون الدخول في خطوات تنفيذها خطوة-بخطوة. لنخرج من المُنقح ونعد تشغيله مرة أخرى: python -m pdb looping.py الآن لنستخدم الأمرnext: (Pdb) next > /Users/sammy/looping.py(5)<module>() -> def nested_loop(): (Pdb) next > /Users/sammy/looping.py(11)<module>() -> if __name__ == '__main__': (Pdb) next > /Users/sammy/looping.py(12)<module>() -> nested_loop() (Pdb) next 500 x y z 600 x y z 700 x y z --Return-- > /Users/sammy/looping.py(12)<module>()->None -> nested_loop() (Pdb) أثناء عملك في تنقيح الشيفرة البرمجية الخاصة بك، قد تريد فحص قيمة متغير، وهو ما تستطيع تنفيذه باستخدام الأمرpp والذي يقوم بطباعة القيمة باستخدام وحدة pprint: (Pdb) pp num_list [500, 600, 700] (Pdb) أغلب الأوامر في مُنقح pdb لديها اختصارات، فمثلا، الأمرstep له اختصار هوs ولأمرnext يوجدn. يسرد أمر المساعدة help كافة الاختصارات لكل الأوامر. بالإضافة لذلك، تستطيع استدعاء الأمر الأخير بالضغط على زر الإدخال (Enter) في لوحة المفاتيح. نقاط الفصل من المؤكد أنك ستعمل على برامج أكبر من المثال المستخدم هنا، لذلك، غالبا ستحتاج أن تتوقف عند وظيفة معينة أو سطر محدد أثناء تنقيحك للشيفرة البرمجية، وذلك بدلا من المرور على كافة أسطر البرنامج. ستتمكن باستخدام الأمرbreak لإعداد نقاط الفصل من تشغيل البرنامج حتى نقطة معينة مسبقا فيه تـسمى نقطة فصل Break point. عندما تضيف نقطة فصل، سيعطيها المُنقح رقما خاصا بها. الأرقام المُعطاة لنقاط الفصل تكون أرقاماً متعاقبة وتبدأ من 1، وتستطيع الإشارة لنقاط الفصل باستخدام أرقامها أثناء عملك في المُنقح. تُضاف نقاط الفصل في أسطر معينة باستخدام الصيغة التالية <program_file>:<line_number> كما هو موضح بالأسفل: (Pdb) break looping.py:5 Breakpoint 1 at /Users/sammy/looping.py:5 (Pdb) اكتب الأمرclear ومن ثم y لإزالة نقاط الفصل الحالية. تستطيع بعد ذلك إضافة نقطة فصل في مكان تعريف الدالة: (Pdb) break looping.nested_loop Breakpoint 1 at /Users/sammy/looping.py:5 (Pdb) تستطيع كذلك إضافة شرط لنقطة الفصل: (Pdb) break looping.py:7, number > 500 Breakpoint 1 at /Users/sammy/looping.py:7 (Pdb) لو أدخلنا الآن الأمرcontinue، فإن البرنامج سيتوقف عندما تكون قيمة المتغيرnumber أكبر من 500. (Pdb) continue 500 x y z > /Users/sammy/looping.py(7)nested_loop() -> print(number) (Pdb) للحصول على قائمة بنقاط الفصل المُعدة حاليا للعمل، استخدم الأمرbreak فقط (دون معطيات) وسوف تحصل على معلومات نقاط الفصل التي قمت بإعدادها. (Pdb) break Num Type Disp Enb Where 1 breakpoint keep yes at /Users/sammy/looping.py:7 stop only if number > 500 breakpoint already hit 2 times (Pdb) نستطيع تعطيل نقطة فصل من البرنامج باستخدام الأمرdisable ومن ثم ندخل رقم نقطة الفصل. في هذه الجلسة (المثال المشروح) قمنا بإضافة نقطة فصل أخرى ومن ثم عطلنا النقطة الأولى: Pdb) break looping.py:11 Breakpoint 2 at /Users/sammy/looping.py:11 (Pdb) disable 1 Disabled breakpoint 1 at /Users/sammy/looping.py:7 (Pdb) break Num Type Disp Enb Where 1 breakpoint keep no at /Users/sammy/looping.py:7 stop only if number > 500 breakpoint already hit 2 times 2 breakpoint keep yes at /Users/sammy/looping.py:11 (Pdb) لتفعيل نقطة الفصل نستخدم الأمرenable، ولإزالة نقطة الفصل نستخدم الأمرclear: (Pdb) enable 1 Enabled breakpoint 1 at /Users/sammy/looping.py:7 (Pdb) clear 2 Deleted breakpoint 2 at /Users/sammy/looping.py:11 (Pdb) تقدّم لك نقاط الفصل في pdb قدرة كبيرة في التحكم، فمثلا، من الإضافات أنك تستطيع تجاهل نقطة الفصل خلال الدورة الحالية من البرنامج باستخدام الأمرignore أو تنفيذ حدث معين عند الوصول لنقطة فصل معينة باستخدام الأمرcommand. تستطيع كذلك إضافة نقاط فصل مؤقتة بواسطة الأمرtbreak، بحيث يقوم المُنقح بحذفها تلقائيا عند الوصول إليها وتنفيذها للمرة الأولى (لإضافة نقطة فصل مؤقتة في السطر رقم 3، ندخل الأمرtbreak 3 ). تضمين المُنقح في الشيفرة البرمجية تستطيع تشغيل جلسة تنقيح في الشيفرة البرمجية مباشرة وذلك باستيراد الوحدة pdb إضافة الدالة pdb.set_trace قبل السطر الذي تريد أن تبدأ الجلسة من عنده. في مثالنا المستخدم خلال هذا المقال، سوف نقوم باستيراد الوحدة pdb واضافة الدالة المذكورة قبل البدء بحلقة التكرار الداخلية في الدالة nested_loop: # Import pdb module import pdb num_list = [500, 600, 700] alpha_list = ['x', 'y', 'z'] def nested_loop(): for number in num_list: print(number) # Trigger debugger at this line pdb.set_trace() for letter in alpha_list: print(letter) if __name__ == '__main__': nested_loop() بإضافة المُنقح في الشيفرة البرمجية الخاصة بك، فأنت لست بحاجة لتشغيل الشيفرة بطريقة معينة أو أن تتذكر إعدادات نقاط الفصل، وستتمكن من تشغيل البرنامج بطريقة عادية وتفعيل المُنقح من خلال التنفيذ. تعديل مسار عمل البرنامج يتيح لك مُنقح بايثون تغيير مسار التنفيذ (Execution Flow) خلال زمن التنفيذ باستخدام الأمرjump، وهذا يعني أنك تستطيع أن تقفز إلى الأمام خلال البرنامج لمنع بعض الشيفرة البرمجية من التنفيذ أو العودة لتنفيذ جزء من الشيفرة مرة أخرى. سوف نعمل على شرح هذه النقطة باستخدام برنامج صغير يقوم بإنشاء متغير من نوع قائمة (List) من الأحرف النصية موجودة ضمن المتغير النصي sammy = "sammy" : def print_sammy(): sammy_list = [] sammy = "sammy" for letter in sammy: sammy_list.append(letter) print(sammy_list) if __name__ == "__main__": print_sammy() إذا قمنا بتشغيل البرنامج بطريقة عادية باستخدام python letters_list.py فإننا سوف نحصل على النتيجة التالية: Output ['s'] ['s', 'a'] ['s', 'a', 'm'] ['s', 'a', 'm', 'm'] ['s', 'a', 'm', 'm', 'y'] لنستعرض كيفية استخدام مُنقح بايثون في تغيير مسار عمل البرنامج السابق بحيث نقوم بالقفز قُدما خلال الشيفرة البرمجية للبرنامج أثناء التشغيل بعد الدورة الأولى من حلقة التكرار: python -m pdb letter_list.py > /Users/sammy/letter_list.py(1)<module>() -> def print_sammy(): (Pdb) list 1 -> def print_sammy(): 2 sammy_list = [] 3 sammy = "sammy" 4 for letter in sammy: 5 sammy_list.append(letter) 6 print(sammy_list) 7 8 if __name__ == "__main__": 9 print_sammy() 10 11 (Pdb) break 5 Breakpoint 1 at /Users/sammy/letter_list.py:5 (Pdb) continue > /Users/sammy/letter_list.py(5)print_sammy() -> sammy_list.append(letter) (Pdb) pp letter 's' (Pdb) continue ['s'] > /Users/sammy/letter_list.py(5)print_sammy() -> sammy_list.append(letter) (Pdb) jump 6 > /Users/sammy/letter_list.py(6)print_sammy() -> print(sammy_list) (Pdb) pp letter 'a' (Pdb) disable 1 Disabled breakpoint 1 at /Users/sammy/letter_list.py:5 (Pdb) continue ['s'] ['s', 'm'] ['s', 'm', 'm'] ['s', 'm', 'm', 'y'] قمنا خلال جلسة التنقيح السابقة بوضع نقطة فصل عند السطر رقم 5 لمنع الشيفرة البرمجية من الاستمرار، ومن ثم قمنا بطباعة بعض الأحرف والاستمرار من خلال الأمرcontinue لإظهار ماذا يحدث. ثم استخدمنا الأمرjump لتجاهل السطر 6. عند هذه النقطة، المتغيرletter يساوي القيمة a ولكننا تجاهلنا السطر الذي يقوم بإضافة هذه القيمة للقائمة. بعد ذلك قمنا بتعطيل نقطة الفصل وتركنا البرنامج يستمر في التنفيذ. في النهاية، فإن الحرفa لم يُضف للقائمة sammy_list. لنعد تشغيل جلسة التنقيح، ونستخدم المُنقح في العودة للخلف خلال التشغيل بهدف إعادة تنفيذ جملة الإضافة للقائمة sammy_list والتي تم تنفيذها خلال التكرار الأول من حلقة التكرار: > /Users/sammy/letter_list.py(1)<module>() -> def print_sammy(): (Pdb) list 1 -> def print_sammy(): 2 sammy_list = [] 3 sammy = "sammy" 4 for letter in sammy: 5 sammy_list.append(letter) 6 print(sammy_list) 7 8 if __name__ == "__main__": 9 print_sammy() 10 11 (Pdb) break 6 Breakpoint 1 at /Users/sammy/letter_list.py:6 (Pdb) continue > /Users/sammy/letter_list.py(6)print_sammy() -> print(sammy_list) (Pdb) pp letter 's' (Pdb) jump 5 > /Users/sammy/letter_list.py(5)print_sammy() -> sammy_list.append(letter) (Pdb) continue > /Users/sammy/letter_list.py(6)print_sammy() -> print(sammy_list) (Pdb) pp letter 's' (Pdb) disable 1 Disabled breakpoint 1 at /Users/sammy/letter_list.py:6 (Pdb) continue ['s', 's'] ['s', 's', 'a'] ['s', 's', 'a', 'm'] ['s', 's', 'a', 'm', 'm'] ['s', 's', 'a', 'm', 'm', 'y'] خلال جلسة التنقيح السابقة، قمنا بإضافة نقطة فصل عند السطر 6، وقمنا بالعودة للخلف للسطر 5 بعد الاستمرار.طبعنا ما تحتويه القائمة خلال التنفيذ وذلك لكي نظهر أن الحرف s قد أٌضيف مرتين. وبعد ذلك قمنا بتعطيل نقطة الفصل وجعلنا البرنامج يستمر في التنفيذ. النتيجة الظاهرة توضح أن الحرف s أضيف مرتين في بداية القائمة sammy_list. يمنع المنقّح بعض استخدامات الأمرjump ، وخاصة عندما يُعدَّل مسار البرنامج للأمام أو الخلفمن خلال جمل لم تُعرَّف. فمثلا، لا تستطيع القفز إلى دوال قبل أن تعرّف المعطيات الخاصة بها، وكذلك لا تستطيع الدخول في وسط جملة try:except أو الخروج من كتلة الشيفرة البرمجية لـ finally. ملخص ما نستطيع ذكره عن الأمرjump، أنك من خلال استخدامه في مُنقح بايثون تستطيع تغيير مسار التنفيذ خلال التنقيح لمعرفة هل هذا التغيير مفيد ضمن ظروف ومعطيات محددة أم لا، وكذلك يساعدك في فهم أصل أو مكان الخلل والمشاكل التي تظهر خلال تنفيذ البرنامج. جدول أوامر المنقح pdb هنا نلخص لكم أوامر المُنقح pdb مع شرح بسيط عما يقدمه كل أمر لكي يساعدك على التذكر خلال تنقيح البرامج على بايثون: الأمر الاختصار العمل help h يقدم قائمة بالأوامر المتاحة أو شرحا لأمر معين jump j تحديد السطر القادم للتنفيذ list l طباعة السياق (ما حول السطر الحالي) للتعرف أكثر على محيط التنفيذ الحالي next n الاستمرار في التنفيذ حتى السطر القادم الذي تصل فيه الدالة الحالية أو تعود step s تنفيذ السطر الحالي والتوقف عند أول فرصة متاحة pp pp طباعة متغير أو تعبير معين quit أو exit q الخروج من البرنامج return r الاستمرار في التنفيذ حتى تعود الدالة الحالية تستطيع أن تقرأ أكثر عن الأوامر وكيفية العمل مع المنقح من خلال الاطلاع على التوثيق الرسمي في بايثون. خاتمة يعد تنقيح البرمجيات خطوة مهمة في أي مشروع لبناء وتطوير برمجية. يقدم مُنقح بايثون بيئة تفاعلية تستطيع الاستفادة منها في تنقيح أي برنامج بايثون. الأوامر المتاحة خلال المنقح تتيح لك وقف البرنامج الخاص بك، والاطلاع على قيم متغيراته خلال التنفيذ وتعديلها إذا رغبت، وكذلك التحكم في مسارات التنفيذ وغيره من الخصائص التي تساعدك في فهم ما يقوم به برنامجك بشكل كامل ولكي تضع يدك على المشاكل والقضايا التي تظهر خلال التنفيذ. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة – بتصرّف – للمقال How To Use the Python Debugger لصاحبته Lisa Tagliaferri. اقرأ أيضًا المقالة التالية: كيفية تنقيح شيفرات بايثون من سطر الأوامر التفاعلي المقالة السابقة: كيفية تطبيق التعددية الشكلية (Polymorphism) على الأصناف المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  16. التعددية الشكلية (Polymorphism) هي القدرة على استخدام واجهة موحدة لعدة أشكال مختلفة، مثل أنواع البيانات أو الأصناف . هذا يسمح للدوال باستخدام كيانات من أنواع مختلفة. بالنسبة للبرامج الكائنية في بايثون، هذا يعني أنه يمكن استخدام كائن معين ينتمي إلى صنف مُعيَّن كما لو كان ينتمي إلى صنف مختلف. تسمح التعددية الشكلية بكتابة شيفرات مرنة ومجرّدَة وسهلة التوسيع والصيانة. سوف تتعلم في هذا الدرس كيفية تطبيق التعددية الشكلية على أصناف بايثون. ما هي التعددية الشكلية؟ التعددية الشكلية هي إحدى السمات الأساسية للأصناف في بايثون، وتُستخدَم عندما تكون هناك توابع لها نفس الأسماء في عدة أصناف، أو أصناف فرعية. يسمح ذلك للدوال باستخدام كائنات من أيٍّ من تلك الأصناف والعمل عليها دون الاكتراث لنوعها. يمكن تنفيذ التعددية الشكلية عبر [الوراثة](رابط المقالة السابقة)، أو باستخدام توابعِ الأصناف الفرعية، أو إعادة تعريفها (overriding). يستخدم بايثون نظام أنواع (typing) خاص، يسمى «نظام التحقق من الأنواع: البطة نموذجًا» (Duck Typing)، وهو حالة خاصة من أنظمة التحقق من الأنواع الديناميكية (Dynamic Typing). يستخدم هذا النظامُ التعدُّديةَ الشكلية، بما في ذلك الربط المتأخر، والإيفاد الديناميكي. يعتمد هذا النظام على «نموذج البطة» بناءً على اقتباسٍ للكاتب جيمس ويتكومب رايلي: خُصِّص هذا المفهوم من قبل مهندس الحاسوب الإيطالي أليكس مارتيلي (Alex Martelli) في رسالة إلى مجموعة comp.lang.python، يقوم نظام التحقق من الأنواع هذا الذي يعتمد البطة نموذجًا على تعريف الكائن من منظور ملاءمة الغرض الذي أُنشِئ لأجله. عند استخدام نظام أنواع عادي، فإنّ ملاءمة الكائن لغرض مُعيَّن يتحدد بنوع الكائن فقط، ولكن في نموذج البطة، يَتحدَّد ذلك بوجود التوابع والخاصيات الضرورية لذلك الغرض بدلًا من النوع الحقيقي للكائن. بمعنى آخر، إذا أردت أن تعرف إن كان الكائن بطةً أم لا، فعليك التحقق مما إذا كان ذلك الكائن يمشي مشي البطة، وصوته كصوت البطة، بدلًا من أن تسأل عما إذا كان الكائن بطةً. عندما تحتوي عدة أصناف أو أصناف فرعية على توابع لها نفس الأسماء، ولكن بسلوكيات مختلفة، نقول إنّ تلك الأصناف متعددة الأشكال (polymorphic) لأنها تستعمل واجهة موحدة يمكن استخدامها مع كيانات من أنواع مختلفة. يمكن للدوال تقييم ومعالجة هذه التوابع متعدِّدة الأشكال دون معرفة أصنافها. إنشاء أصناف متعددة الأشكال للاستفادة من التَعدُّدية الشكلية، سننشئ صنفين مختلفين لاستخدامهما مع كائنين مختلفين. يحتاج هذان الصنفان المختلفان واجهة موحدة يمكن استخدامها بطريقة تعدُّدية الشكل (polymorphically)، لذلك سنعرّف فيهما توابع مختلفة، ولكن لها نفس الاسم. سننشئ صنفًا باسم ‎Shark‎ وصنفُا آخر باسم ‎Clownfish‎، وسيُعرِّف كل منهما التوابع ‎swim()‎ و ‎swim_backwards()‎ و ‎skeleton()‎. class Shark(): def swim(self): print("القرش يسبح.") def swim_backwards(self): print("لا يمكن للقرش أن يسبح إلى الوراء، لكن يمكنه أن يغوص إلى الوراء.") def skeleton(self): print("هيكل القرش مصنوع من الغضروف.") class Clownfish(): def swim(self): print("سمكة المهرج تسبح.") def swim_backwards(self): print("يمكن لسمكة المهرج أن تسبح إلى الخلف.") def skeleton(self): print("هيكل سمكة المهرج مصنوع من العظام.") في الشيفرة أعلاه، لدى الصنفين ‎Shark‎ و ‎Clownfish‎ ثلاث توابع تحمل نفس الاسم بيْد أنّ وظائف تلك التوابع تختلف من صنف لآخر. دعنا نستنسخ (instantiate) من هذين الصنفين كائنين: ... sammy = Shark() sammy.skeleton() casey = Clownfish() casey.skeleton() عند تنفيذ البرنامج باستخدام الأمر ‎python polymorphic_fish.py‎، يمكننا أن نرى أنّ كل كائن يتصرف كما هو متوقع: هيكل القرش مصنوع من الغضروف. هيكل سمكة المهرج مصنوع من العظام. الآن وقد أصبح لدينا كائنان يستخدمان نفس الواجهة، فبمقدورنا استخدام هذين الكائنين بنفس الطريقة بغض النظر عن نوعيهما. التعددية الشكلية في توابع الأصناف لإظهار كيف يمكن لبايثون استخدام الصنفين المختلفين اللذين عرّفناهما أعلاه بنفس الطريقة، سننشئ أولاً حلقة for، والتي ستمر على صف من الكائنات. ثم سنستدعي التوابع بغض النظر عن نوع الصنف الذي ينتمي إليه كل كائن. إلا أننا سنفترض أنّ تلك التوابع موجودة في كل تلك الأصناف. ... sammy = Shark() casey = Clownfish() for fish in (sammy, casey): fish.swim() fish.swim_backwards() fish.skeleton() لدينا كائنان، ‎sammy‎ من الصنف ‎Shark‎، و ‎casey‎ من الصنف ‎Clownfish‎. تمر حلقة ‎for‎ على هذين الكائنين، وتستدعي التوابع ‎swim()‎ و ‎swim_backwards()‎ و ‎skeleton()‎ على كل منها. عند تنفيذ البرنامج، سنحصل على المخرجات التالية: القرش يسبح. لا يمكن للقرش أن يسبح إلى الوراء، لكن يمكنه أن يغوص إلى الوراء. هيكل القرش مصنوع من الغضروف. سمكة المهرج تسبح. يمكن لسمكة المهرج أن تسبح إلى الخلف. هيكل سمكة المهرج مصنوع من العظام. مرت الحلقة ‎for‎ على الكائن ‎sammy‎ من الصنف ‎Shark‎، ثم على الكائن ‎casey‎ المنتمي إلى الصنف ‎Clownfish‎، لذلك نرى التوابع الخاصة بالصنف ‎Shark‎ قبل التوابع الخاصة بالصنف ‎Clownfish‎. يدلُّ هذا على أنَّ بايثون تستخدم هذه التوابع دون أن تعرف أو تعبأ بتحديد نوع الصنف الخاص بالكائنات. وهذا مثال حي على استخدام التوابع بطريقة مُتعدِّدَة الأشكال. التعددية الشكلية في الدوال يمكننا أيضًا إنشاء دالة تقبل أيّ شيء، وهذا سيسمح باستخدام التعددية الشكلية. لننشئ دالة تسمى ‎in_the_pacific()‎، والتي تأخذ كائنًا يمكننا تسميته ‎fish‎. رغم أننا سنستخدم الاسم ‎fish‎، إلا أنه يمكننا استدعاء أي كائن في هذه الدالة: … def in_the_pacific(fish): بعد ذلك، سنجعل الدالة تستخدم الكائن ‎fish‎ الذي مرّرناه إليها. وفي هذه الحالة، سنستدعي التابع ‎swim()‎ المعرّف في كل من الصنفين ‎Shark‎ و ‎Clownfish‎: ... def in_the_pacific(fish): fish.swim() بعد ذلك، سننشئ نسخًا (instantiations) من الصنفين ‎Shark‎ و ‎Clownfish‎ لنمرّرهما بعد ذلك إلى نفس الدالة ‎in_the_pacific()‎: ... def in_the_pacific(fish): fish.swim() sammy = Shark() casey = Clownfish() in_the_pacific(sammy) in_the_pacific(casey) عند تنفيذ البرنامج، سنحصل على المخرجات التالية: القرش يسبح. سمكة المهرج تسبح. رغم أننا مرّرنا كائنًا عشوائيًا (‎fish‎) إلى الدالة ‎in_the_pacific()‎ عند تعريفها، إلا أننا ما زلنا قادرين على استخدامها استخدامًا فعالًا، وتمرير نسخ من الصنفين ‎Shark‎ و ‎Clownfish‎ إليها. استدعى الكائنُ ‎casey‎ التابعَ ‎swim()‎ المُعرَّف في الصنف ‎Clownfish‎، فيما استدعى الكائنُ ‎sammy‎ التابعَ ‎swim()‎ المُعرَّف في الصنف ‎Shark‎. خلاصة تسمح التعدُّدية الشكلية باستخدام الكائنات بغض النظر عن نوعها، وهذا يوفر لبايثون مرونة كبيرة، وقابلية لتوسيع الشيفرة الكائنية. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة -وبتصرّف- للمقال How To Apply Polymorphism to Classes in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة التالية: كيف تستخدم منقح بايثون المقالة السابقة: وراثة الأصناف في بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  17. تسمح البرمجة الكائنية باستخدام متغيرات على مستوى الصنف، أو على مستوى النسخة (instance). المتغيرات هي رموز (symbols) تدل على قيمة تستخدمها في برنامجك. يشار إلى المتغيرات على مستوى الصنف باسم متغيرات الصنف (class variables)، في حين تسمى المتغيرات الموجودة على مستوى النسخة متغيرات النسخة (instance variables). إذا توقعت أن يكون المتغير متسقًا في جميع نسخ الصنف، أو عندما تود تهيئة المتغير، فالأفضل أن تُعرِّف ذلك المتغير على مستوى الصنف. أما إن كنت تعلم أن المتغير سيختلف من نسخة إلى أخرى، فالأفضل أن تعرفه على مستوى النسخة. يسعى أحد مبادئ تطوير البرمجيات هو مبدأ DRY (اختصارًا للعبارة don’t repeat yourself، والذي يعني لا تكرر نفسك) إلى الحد من التكرار في الشيفرة. أي تلتزم البرمجة الكائنية بمبدأ DRY على تقليل التكرار في الشيفرة. ستتعلم في هذا الدرس كيفية استخدام متغيرات الصنف والنسخة في البرمجة الكائنية في بايثون. متغيرات الصنف تٌعرَّف متغيرات الصنف داخل الصنف وخارج كل توابعه وعادةً ما توضع مباشرة أسفل ترويسة الصنف، وقبل الباني (constructor) والتوابع الأخرى. ولمَّا كانت مملوكة للصنف نفسه، فستُشارَك مع جميع نُسَخ ذلك الصنف. وبالتالي، سيكون لها نفس القيمة بغض النظر عن النسخة، إلا إن كنت ستستخدم متغير الصنف لتهيئة متغير معيَّن. متغير الصنف يبدو كما يلي: class Shark: animal_type = "fish" في الشيفرة أعلاه أحلنا القيمة "fish" إلى المتغير animal_type. يمكننا إنشاء نسخة من الصنف Shark (سنطلق عليها new_shark)، ونطبع المتغير باستخدام الصياغة النقطية (dot notation): class Shark: animal_type = "fish" new_shark = Shark() print(new_shark.animal_type) لننفذ البرنامج: python shark.py سيعيد البرنامج قيمة المتغير: fish دعنا نضيف مزيدًا من متغيرات الصنف، ونطبعها: class Shark: animal_type = "fish" location = "ocean" followers = 5 new_shark = Shark() print(new_shark.animal_type) print(new_shark.location) print(new_shark.followers) يمكن أن تتألف متغيرات الصنف من أي نوع من البيانات المتاحة في بايثون تمامًا مثل أي متغير آخر. استخدمنا في هذا البرنامج السلاسل النصية والأعداد الصحيحة. لننفذ البرنامج مرة أخرى باستخدام الأمر python shark.py ونرى المخرجات: fish ocean 5 يمكن للنسخة new_shark الوصول إلى جميع متغيرات الصنف وطباعتها عند تنفيذ البرنامج. تُنشَأ متغيرات الصنف عند إنشاء الصنف مباشرةً (وليس عند إنشاء نسخة منه) وتحتل موضعًا لها في الذاكرة ويمكن لأي كائن مُشتَق (نسخة) من الصنف نفسه أن يصل إليها ويقرأ قيمتها. متغيرات النسخة تختلف متغيرات النسخة عن متغيرات الصنف أن النسخة المشتقة من الصنف هي من تملكها وليس الصنف نفسه أي تكون على مستوى النسخة وسيُنشَأ متغير مستقل في الذاكرة عند إنشاء كل نسخة. هذا يعني أنّ متغيرات النسخة ستختلف من كائن إلى آخر. تُعرَّف متغيرات النسخة ضمن التوابع على خلاف متغيرات الصنف. في مثال الصنف Shark أدناه، عّرفنا متغيري النسخة name و age: class Shark: def __init__(self, name, age): self.name = name self.age = age عندما ننشئ كائنًا من الصنف Shark، سيتعيّن علينا تعريف هذه المتغيرات، عبر تمريرها كمعاملات ضمن الباني (constructor)، أو أي تابع آخر. class Shark: def __init__(self, name, age): self.name = name self.age = age new_shark = Shark("Sammy", 5) كما هو الحال مع متغيرات الأصناف، يمكننا بالمثل طباعة متغيرات النسخة: class Shark: def __init__(self, name, age): self.name = name self.age = age new_shark = Shark("Sammy", 5) print(new_shark.name) print(new_shark.age) عند تنفيذ البرنامج أعلاه باستخدام python shark.py، سنحصل على المخرجات التالية: Sammy 5 تتألف المخرجات التي حصلنا عليها من قيم المتغيرات التي هيّأناها لأجل الكائن new_shark. لننشئ كائنًا آخر من الصنف Shark يسمى stevie: class Shark: def __init__(self, name, age): self.name = name self.age = age new_shark = Shark("Sammy", 5) print(new_shark.name) print(new_shark.age) stevie = Shark("Stevie", 8) print(stevie.name) print(stevie.age) يمرِّر الكائن stevie المعاملات إلى الباني لتعيين قيم متغيرات النسخة الخاصة به. تسمح متغيرات النسخة، المملوكة لكائنات الصنف، لكل كائن أو نسخة أن تكون لها متغيرات خاصة بها ذات قيم مختلفة عن بعضها بعضًا. العمل مع متغيرات الصنف والنسخة معًا غالبًا ما تُستخدم متغيرات الصنف ومتغيرات النسخة في نفس الشيفرة، ويوضح المثال التالي يستخدم الصنف Shark الذي أنشأناه سابقًا هذا الأمر. تشرح التعليقات في البرنامج كل خطوة من خطوات العملية. class Shark: # متغيرات الصنف animal_type = "fish" location = "ocean" # name و age باني مع متغيري النسخة def __init__(self, name, age): self.name = name self.age = age # followers تابع مع متغير النسخة def set_followers(self, followers): print("This user has " + str(followers) + " followers") def main(): # الكائن الأول، إعداد متغيرات النسخة في الباني sammy = Shark("Sammy", 5) # name طباعة متغير النسخة print(sammy.name) # location طباعة متغير الصنف print(sammy.location) # الكائن الثاني stevie = Shark("Stevie", 8) # name طباعة متغير النسخة print(stevie.name) # followers لتمرير متغير النسخة set_followers استخدام التابع stevie.set_followers(77) # animal_type طباعة متغير الصنف print(stevie.animal_type) if __name__ == "__main__": main() عند تنفيذ البرنامج باستخدام python shark.py، سنحصل على المخرجات التالية: Sammy ocean Stevie This user has 77 followers fish خلاصة في البرمجة الكائنية، يشار إلى المتغيرات المُعرَّفة على مستوى الصنف بمتغيرات الصنف، في حين تسمى المتغيرات المُعرّفة على مستوى الكائن بمتغيرات النسخة. يتيح لنا هذا التمييز استخدام متغيرات ذات قيم واحدة بينها عبر متغيرات الصنف، أو استخدام متغيرات مختلفة لكل كائن على حدة عبر متغيرات النسخة. كما يضمن استخدام المتغيرات الخاصة بالصنف أو النسخة أن تكون الشيفرة متوافقة مع مبدأ DRY. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة -وبتصرّف- للمقال Understanding Class and Instance Variables in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضا المقالة التالية: وراثة الأصناف في بايثون 3 المقالة السابقة: كيفية إنشاء الأصناف وتعريف الكائنات في بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  18. الدالة (funtion) هي كتلة من التعليمات التي تنفِّذ إجراءً ما، ويمكن، بعد تعريفها، إعادة استخدامها في أكثر من موضع. تجعل الدوال الشيفرة تركيبية (modular)، مما يسمح باستخدام نفس الشفرة مرارًا وتكرارًا. تضم بايثون عددًا من الدوال المُضمّنة الشائعة، مثل: ‎print()‎: تطبع كائنًا في الطرفية. ‎int()‎: تحوّل أنواع البيانات النصية أو العددية إلى أعداد صحيحة. ‎len()‎: تعيد طول كائن، وغيرها من الدوال. تتضمن أسماء الدوال الأقواس، وقد تتضمن معاملات أيضًا. في هذا الدرس، سنتعلم كيفية تعريف الدوال، وكيفية استخدامها في البرامج. تعريف الدالة لنبدأ بتحويل البرنامج "مرحبًا بالعالم!" إلى دالة. أنشئ ملفًا نصيًا جديدًا، وافتحه في محرر النصوص المفضل عندك، ثم استدع البرنامج ‎hello.py‎. تُعرَّف الدالة باستخدام الكلمة المفتاحية ‎def‎، متبوعة باسم من اختيارك، متبوعًا بقوسين يمكن أن يَحتويا المعاملات التي ستأخذها الدالة، ثم ينتهي التعريف بنقطتين. في هذه الحالة، سنعرّف دالة باسم ‎hello()‎: def hello(): في الشفرة أعلاه، أعددنا السطر الأول من تعريف الدالة. بعد هذا، سنضيف سطرًا ثانيًا مُزاحًا بأربع مسافات بيضاء، وفيه سنكتب التعليمات التي ستنفّذها الدالة. في هذه الحالة، سنطبع العبارة مرحبا بالعالم في سطر الأوامر: def hello(): print("مرحبا بالعالم") لقد أتممنا تعريف دالتنا، غير أننا إن نَفَّذنا البرنامج الآن، فلن يحدث أيّ شيء، لأننا لم نستدع الدالة؛ لذلك، سنستدع الدالة عبر التعبير ‎hello()‎ خارج كتلة تعريف الدالة: def hello(): print("مرحبا بالعالم") hello() الآن، لننفّذ البرنامج: python hello.py يجب أن تحصل على المخرجات التالية: مرحبا بالعالم! بعض الدوال أكثر تعقيدًا بكثير من الدالة ‎hello()‎ التي عرّفناها أعلاه. على سبيل المثال، يمكننا استخدام for والتعليمات الشرطية، وغيرها داخل كتلة الدالة. على سبيل المثال، تستخدم الدالة المُعرّفة أدناه تعليمة شرطية للتحقق مما إذا كانت المدخلات الممرّرة إلى المتغير ‎name‎ تحتوي على حرف علة (vowel)، ثم تستخدم الحلقة ‎for‎ للمرور (iterate) على الحروف الموجودة في السلسلة النصية ‎name‎. # names() تعريف الدالة def names(): # وإحالة المدخلات عليه name إعداد المتغير name = str(input('أدخل اسمك:')) # يحتوي حرف علة name التحقق من أن if set('aeiou').intersection(name.lower()): print('اسمك يحوي حرف علة') else: print('اسمك لا يحوي حرف علة') # name المرور على حروف for letter in name: print(letter) # استدعاء الدالة names() تستخدم الدالة ‎names()‎ التي عرّفناها أعلاه تعليمة شرطية، وحلقة for، وهذا توضيح لكيفية تنظيم الشفرة البرمجية ضمن تعريف الدالة. يمكننا أيضًا جعل التعليمة الشرطية والحلقة ‎for‎ دالتين منفصلتين. إنّ تعريف الدوال داخل البرامج يجعل الشفرة البرمجية تركيبية (modular)، وقابلة لإعادة الاستخدام، وذلك سيتيح لنا استدعاء نفس الدالة دون إعادة كتابة شيفرتها كل مرة. المعاملات حتى الآن، عرّفنا دالة ذات قوسين فارغين لا تأخذ أيّ وسائط (arguments)، سنتعلم في هذا القسم كيفية تعريف المعاملات (parameters) وتمرير البيانات إلى الدوال. المعامل (parameter) هو كيان مُسمًّى يوضع في تعريف الدالة، ويعرّف وسيطًا (arguments) يمكن أن تقبله الدالة عند استدعائها. دعنا ننشئ برنامجًا صغيرًا يأخذ 3 معاملات ‎x‎ و ‎y‎ و ‎z‎. سننشئ دالة تجمع تلك المعاملات وفق عدة مجموعات ثم تطبع تلك حاصل جمعها. def add_numbers(x, y, z): a = x + y b = x + z c = y + z print(a, b, c) add_numbers(1, 2, 3) مرّرنا العدد ‎1‎ إلى المعامل ‎x‎، و ‎2‎ إلى المعامل ‎y‎، و ‎3‎ إلى المعامل ‎z‎. تتوافق هذه القيم مع المعاملات المقابلة لها في ترتيب الظهور. يُجرِي البرنامج العمليات الحسابية على المعاملات على النحو التالي: a = 1 + 2 b = 1 + 3 c = 2 + 3 تطبع الدالة أيضًا ‎a‎ و ‎b‎ و ‎c‎، وبناءً على العمليات الحسابية أعلاه، فإنّ قيمة ‎a‎ ستساوي العدد ‎3‎، و ‎b‎ ستساوي ‎4‎، و ‎c‎ ستساوي العدد ‎5‎. لننفّذ البرنامج: python add_numbers.py سنحصل على المخرجات التالية: 3 4 5 المعاملات هي وسائط يتم تعريفها عادة كمتغيرات ضمن تعريف الدالة. يمكن تعيين قيم إليها عند تنفيذ التابع بتمرير وسائط إلى الدالة. دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن الوسائط المسمّاة تُستدعى المعاملات بحسب ترتيب ظهورها في تعريف الدالة، أما الوسائط المسماة (Keyword Arguments) فتُستخدَم بأسمائها في استدعاء الدالة. عند استخدام الوسائط المسمّاة، يمكنك استخدام المعاملات بأيّ ترتيب تريد، لأنّ مترجم بايثون سيستخدم الكلمات المفتاحية لمطابقة القيم مع المعاملات. سننشئ دالة تعرض معلومات الملف الشخصي للمستخدم، ونمرر إليها المُعامِلين ‎username‎ (سلسلة نصية)، و ‎followers‎ (عدد صحيح). # تعريف دالة ذات معاملات def profile_info(username, followers): print("Username: " + username) print("Followers: " + str(followers)) داخل تعريف الدالة، وضعنا ‎username‎ و ‎followers‎ بين قوسي الدالة ‎profile_info()‎ أثناء تعريفها. تطبع شفرة الدالة المعلومات الخاصة بالمستخدم على هيئة سلسلة نصية باستخدام المعاملين المُمرّرين. الآن، يمكننا استدعاء الدالة وتعيين المعاملات: def profile_info(username, followers): print("Username: " + username) print("Followers: " + str(followers)) # استدعاء الدالة مع تعيين المعاملات profile_info("sammyshark", 945) # استدعاء الدالة مع تمرير الوسائط المسماة إليها profile_info(username="AlexAnglerfish", followers=342) في الاستدعاء الأول للدالة، مرّرنا اسم المستخدم ‎sammyshark‎، وعدد المتابعين ‎945‎ بالترتيب الوارد في تعريف الدالة. أمّا في الاستدعاء الثاني للدالة، فقد استخدمنا الوسائط المسمّاة، وقمنا بتعيين قيم للوسائط ويمكن عكس الترتيب إن شئنا. لننفذ البرنامج: python profile.py سنحصل على المخرجات التالية: Username: sammyshark Followers: 945 Username: AlexAnglerfish Followers: 342 سنحصل في المخرجات على أسماء المستخدمين، وأعداد المتابعين لكلا المستخدمين. يمكننا تغيير ترتيب المعاملات، كما في المثال التالي: def profile_info(username, followers): print("Username: " + username) print("Followers: " + str(followers)) # تغيير ترتيب المعاملات profile_info(followers=820, username="cameron-catfish") عند تنفيذ البرنامج أعلاه، سنحصل على المخرجات التالية: Username: cameron-catfish Followers: 820 يحافظ تعريف الدالة على نفس ترتيب العبارات في ‎print()‎، لذلك يمكننا استخدام الوسائط المسمّاة بأيّ ترتيب نشاء. القيم الافتراضية للوسائط يمكننا إعطاء قيم افتراضية لواحد أو أكثر من المعاملات. في المثال أدناه، سنعطي للمعامل ‎followers‎ القيمة الافتراضية ‎1‎ لاستعمالها إن لم تُمرَّر هذه القيمة للدالة عند استدعائها: def profile_info(username, followers=1): print("Username: " + username) print("Followers: " + str(followers)) الآن، يمكننا استدعاء الدالة مع تعيين اسم المستخدم فقط، وسيُعيّن عدد المتابعين تلقائيًا ويأخذ القيمة 1. لكن يمكننا تغيير عدد المتابعين إن شئنا. def profile_info(username, followers=1): print("Username: " + username) print("Followers: " + str(followers)) profile_info(username="JOctopus") profile_info(username="sammyshark", followers=945) عندما ننفّذ البرنامج باستخدام الأمر ‎python profile.py‎، سنحصل على المخرجات التالية: Username: JOctopus Followers: 1 Username: sammyshark Followers: 945 تمرير قيم إلى المعاملات الافتراضية سيتخطى القيمة الافتراضية المعطاة في تعريف الدالة. إعادة قيمة كما يمكن تمرير قيم إلى الدالة، فيمكن كذلك أن تنتج الدالة قيمة وتعيدها لم استدعاها. يمكن أن تنتج الدالة قيمة، ويكونُ ذلك عبر استخدام التعليمة ‎return‎، هذه التعليمة اختيارية، وفي حال استخدامها، فستُنهِي الدالة مباشرةً عملها وتوقف تنفيذها، وتُمرَّر قيمة التعبير الذي يعقُبها إلى المُستدعي (caller). إذا لم يلي التعليمة ‎return‎ أي شيء، فستُعيد الدالة القيمةَ ‎None‎. حتى الآن، استخدمنا الدالة ‎print()‎ بدلاً من ‎return‎ في دوالنا لطباعة شيء بدلًا من إعادته. لننشئ برنامجًا يعيد متغيرًا بدلًا من طباعته الآن. في ملف نصي جديد يسمى ‎square.py‎، سننشئ برنامجًا يحسب مربع المعامل ‎x‎، ويُحيل الناتج إلى المتغير ‎y‎، ثم يعيده. سنطبع المتغير ‎result‎، والذي يساوي ناتج تنفيذ الدالة ‎square(3)‎. def square(x): y = x ** 2 return y result = square(3) print(result) لننفّذ البرنامج: python square.py سنحصل على المخرجات التالية: 9 مخرجات البرنامج هي العدد الصحيح ‎9‎ الذي أعادته الدالة وهو ما نتوقعه لو طلبنا من بايثون حساب مربع العدد 3. لفهم كيفية عمل التعليمة ‎return‎، يمكننا تعليق التعليمة ‎return‎: def square(x): y = x ** 2 # return y result = square(3) print(result) الآن، لننفّذ البرنامج مرة أخرى: python square.py سنحصل على الناتج التالي: None بدون استخدام التعليمة ‎return‎، لا يمكن للبرنامج إعادة أيّ قيمة، لذلك تُعاد القيمة الافتراضية ‎None‎. إليك مثال آخر، في برنامج ‎add_numbers.py‎ أعلاه، سنستبدل بالتعليمة ‎return‎ الدالة ‎print()‎. def add_numbers(x, y, z): a = x + y b = x + z c = y + z return a, b, c sums = add_numbers(1, 2, 3) print(sums) خارج الدالة، أحلنا إلى المتغير ‎sums‎ نتيجة استدعاء الدالة بالوسائط ‎1‎ و ‎2‎ و ‎3‎ كما فعلنا أعلاه ثم طبعنا قيمته. فلننفّذ البرنامج مرة أخرى: python add_numbers.py والناتج سيكون: (3, 4, 5) لقد حصلنا على الأعداد ‎3‎ و ‎4‎ و ‎5‎ وهي نفس المخرجات التي تلقيناها سابقًا عندما استخدمنا الدالة ‎print()‎ في الدالة. هذه المرة تمت إعادتها على هيئة صف لأنّ التعبير المرافق للتعليمة ‎return‎ يحتوي على فاصلة واحدة على الأقل. تُوقَف الدوال فورًا عندما تصل إلى التعليمة ‎return‎، سواء أعادت قيمة، أم لم تُعِد. def loop_five(): for x in range(0, 25): print(x) if x == 5: # x == 5 إيقاف الدالة عند return print("This line will not execute.") loop_five() يؤدي استخدام التعليمة ‎return‎ داخل الحلقة ‎for‎ إلى إنهاء الدالة، وبالتالي لن يتم تنفيذ السطر الموجود خارج الحلقة. لو استخدمنا بدلًا من ذلك break، فسيُنفّذ السطر ‎print()‎ الأخير من المثال السابق. نعيد التذكير أنَّ التعليمة ‎return‎ تنهي عمل الدالة، وقد تعيد قيمة إذا أعقبها تعبير. استخدام ‎main()‎ دالةً رغم أنه يمكنك في بايثون استدعاء الدالة في أسفل البرنامج، وسيتم تنفيذها (كما فعلنا في الأمثلة أعلاه)، فإنّ العديد من لغات البرمجة (مثل C++‎ و Java) تتطلب الدالة ‎main‎. إنّ تضمين دالة ‎main()‎، وإن لم يكن إلزاميًا، يمكن أن يهيكل برامج بيثون بطريقة منطقية، بحيث تضع أهم مكونات البرنامج في دالة واحدة. كما يمكن أن يجعل البرنامج أكثر مقروئية للمبرمجين غير البايثونيِّين. سنبدأ بإضافة دالة ‎main()‎ إلى برنامج ‎hello.py‎ أعلاه. سنحتفظ بالدالة ‎hello()‎، ثم نعرّف دالة ‎main()‎: def hello(): print("مرحبا بالعالم") def main(): ضمن الدالة ‎main()‎، سندرج الدالة ‎print()‎، والتي ستعُلِمنا بأننا في الدالة ‎main()‎. أيضًا سنستدعي الدالة ‎hello()‎ داخل ‎main()‎: def hello(): print("مرحبا بالعالم") def main(): print("هذه هي الدالة الرئيسية") hello() أخيرًا، في أسفل البرنامج، سنستدعي الدالة ‎main()‎: def hello(): print("مرحبا بالعالم") def main(): print("هذه هي الدالة الرئيسية.") hello() main() الآن يمكننا تنفيذ برنامجنا: python hello.py وسنحصل على المخرجات التالية: هذه هي الدالة الرئيسية. مرحبا بالعالم! لمّا استدعينا الدالة ‎hello()‎ داخل ‎main()‎، ثم نفّذنا الدالة ‎main()‎ وحدها، فقد طُبع النص مرحبا بالعالم مرة واحدة فقط، وذلك عقب السلسلة النصية التي أخبرتنا بأننا في الدالة الرئيسية. سنعمل الآن مع دوال مُتعدِّدة، لذلك من المستحسن أن تراجع نطاقات المتغيرات في المقالة: كيفية استخدام المتغيرات في بايثون. إذا عرّفت متغيرًا داخل دالة، فلا يمكنك أن تستخدم ذلك المتغير إلا ضمن تلك الدالة. لذا، إن أردت استخدام متغير ما في عدة دوال، فقد يكون من الأفضل الإعلان عنه متغيرًا عامًا (global variable). في بايثون، يعدُّ ‎'__main__'‎ اسم النطاق الذي ستُنفَّذ فيه الشيفرة العليا (top-level code). عند تنفيذ برنامج من الدخل القياسي (standard input)، أو من سكربت، أو من سطر الأوامر، سيتم ضبط ‎__name__‎ عند القيمة ‎'__main__'‎. لهذا السبب، اصطلح مطورو بايثون على استخدام الصياغة التالية: if __name__ == '__main__': # الشفرة التي ستُنفّذ لو كان هذا هو البرنامج الرئيسي هذه الصياغة تتيح استخدام ملفات بايثون إما: برامج رئيسية، مع تنفيذ ما يلي التعليمة ‎if‎، أو وحدات عادية، مع عدم تنفيذ ما يتبع التعليمة ‎if‎. سيتم تنفيذ الشفرة غير المُتضمّنة في العبارة if __name__ == '__main__'‎:‎ عند التنفيذ. إذا كنت تستخدم ملف بايثون كوحدة، فسيتم أيضًا تنفيذ الشفرة البرمجية غير المُتضمّنة في هذه العبارة عند استيراد ذلك الملف. دعنا نوسع البرنامج ‎names.py‎ أعلاه، سننشئ ملفا جديدًا يسمى ‎more_names.py‎. سنعلن في هذا البرنامج عن متغير عام، ونعدِّل الدالة ‎names()‎ الأصليَّة بشكل نقسِّم فيه التعليمات إلى دالّتين منفصلتين. ستتحقق الدالة الأولى ‎has_vowel()‎ مما إذا كانت السلسلة النصية ‎name‎ تحتوي على حرف علة (vowel). وتطبع الدالة الثانية ‎print_letters()‎ كل حرف من السلسلة النصية ‎name‎. # الإعلان عن متغير عام لاستخدامه في جميع الدوال name = str(input('أدخل اسمك:')) # يحتوي حرف علة name تعريف دالة للتحقق من أن def has_vowel(): if set('aeiou').intersection(name.lower()): print('اسمك يحتوي حرف علة') else: print('اسمك لا يحتوي حرف علة') # name المرور على حروف def print_letters(): for letter in name: print(letter) بعد ذلك، دعنا نعرّف الدالة ‎main()‎ التي سَتستدعي كلا الدّالتين ‎has_vowel()‎ و ‎print_letters()‎. # الإعلان عن متغير عام لاستخدامه في جميع الدوال name = str(input('أدخل اسمك:')) # يحتوي حرف علة name تعريف دالة للتحقق من أنّ def has_vowel(): if set('aeiou').intersection(name.lower()): print('اسمك يحتوي حرف علة') else: print('اسمك لا يحتوي حرف علة') # name المرور على حروف def print_letters(): for letter in name: print(letter) # التي ستستدعي بقية الدوال main تعريف الدالة def main(): has_vowel() print_letters() أخيرًا، سنضيف العبارة ‎if __name__ == '__main__':‎ في أسفل الملف. لقد وضعنا جميع الدوال التي نودّ تنفيذها في الدالة ‎main()‎، لذا سنستدعي الدالة ‎main()‎ بعد العبارة ‎if‎. # الإعلان عن متغير عام لاستخدامه في جميع الدوال name = str(input('أدخل اسمك:')) # يحتوي حرف علة name تعريف دالة للتحقق من أن def has_vowel(): if set('aeiou').intersection(name.lower()): print('اسمك يحتوي حرف علة') else: print('اسمك لا يحتوي حرف علة') # name المرور على حروف def print_letters(): for letter in name: print(letter) # التي ستستدعي بقية الدوال main تعريف الدالة def main(): has_vowel() print_letters() # main() تنفيذ الدالة if __name__ == '__main__': main() يمكننا الآن تنفيذ البرنامج: python more_names.py سيعرض هذا البرنامج نفس المخرجات التي عرضها البرنامج ‎names.py‎، بيْد أنّ الشفرة هنا أكثر تنظيمًا، ويمكن استخدامها بطريقة تركيبية (modular). إذا لم ترغب في الإعلان عن الدالة ‎main()‎، يمكنك بدلاً من ذلك إنهاء البرنامج كما يلي: ... if __name__ == '__main__': has_vowel() print_letters() يؤدي استخدام ‎main()‎ كدالة، واستخدام العبارة ‎if __name__ == '__main__':‎ إلى تنظيم الشيفرة البرمجية بطريقة منطقية، وجعلها أكثر مقروئية وتراكبية. خلاصة الدوال هي كتل من التعليمات البرمجية التي تُنفِّذ إجراءات معيّنة داخل البرنامج، كما تساعد على جعل الشفرة تركيبية، وقابلة لإعادة الاستخدام بالإضافة إلى أنها تنظمها وتسهل من قراءتها. لمعرفة المزيد حول كيفية جعل الشفرة تركيبية، يمكنك قراءة المقالة التالية: كيفية كتابة الوحدات في بايثون 3. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة -وبتصرّف- للمقال How To Define Functions in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضا المقالة التالية: كيفية استخدام ‎args و ‎*kwargs في بايثون 3 المقالة السابقة: كيفية استخدام تعابير break و continue و pass عند التعامل مع حلقات التكرار في بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  19. يسمح لك استخدام حلقات for أو while في بايثون بأتمتة وتكرار المهام بطريقة فعّالة. لكن في بعض الأحيان، قد يتدخل عامل خارجي في طريقة تشغيل برنامجك، وعندما يحدث ذلك، فربما تريد من برنامجك الخروج تمامًا من حلقة التكرار، أو تجاوز جزء من الحلقة قبل إكمال تنفيذها، أو تجاهل هذا العامل الخارجي تمامًا. لذا يمكنك فعل ما سبق باستخدام تعابير break و continue و pass. التعبير break يوفِّر لك التعبير break القدرة على الخروج من حلقة التكرار عند حدوث عامل خارجي. حيث عليك وضع التعبير break في الشيفرة التي ستُنفَّذ في كل تكرار للحلقة، ويوضع عادةً ضمن تعبير if. ألقِ نظرةً إلى أحد الأمثلة الذي يستعمل التعبير break داخل حلقة for: number = 0 for number in range(10): number = number + 1 if number == 5: break # break here print('Number is ' + str(number)) print('Out of loop') هذا برنامجٌ صغيرٌ، هيّأنا في بدايته المتغير number بجعله يساوي الصفر، ثم بنينا حلقة تكرار for التي تعمل لطالما كانت قيمة المتغير number أصغر من 10. ثم قمنا بزيادة قيمة المتغير number داخل حلقة for بمقدار 1 في كل تكرار، وذلك في السطر number = number + 1. ثم كانت هنالك عبارة if التي تختبر إن كان المتغير number مساوٍ للرقم 5، وعند حدوث ذلك فسيُنفَّذ التعبير break للخروج من الحلقة. وتوجد داخل حلقة التكرار الدالة print()‎ التي تُنفَّذ في كل تكرار إلى أن نخرج من الحلقة عبر التعبير break، وذلك لأنَّها موجودة بعد التعبير break. لكي نتأكد أننا خرجنا من الحلقة، فوضعنا عبارة print()‎ أخيرة موجودة خارج حلقة for. سنرى الناتج الآتي عند تنفيذ البرنامج: Number is 1 Number is 2 Number is 3 Number is 4 Out of loop الناتج السابق يُظهِر أنَّه بمجرد أن أصبح العدد الصحيح number مساويًا للرقم 5، فسينتهي تنفيذ حلقة التكرار عبر التعبير break. الخلاصة: التعبير break يؤدي إلى الخروج من حلقة التكرار. دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن التعبير continue التعبير continue يسمح لنا بتخطي جزء من حلقة التكرار عند حدوث عامل خارجي، لكن إكمال بقية الحلقة إلى نهايتها. بعبارةٍ أخرى: سينتقل تنفيذ البرنامج إلى أوّل حلقة التكرار عند تنفيذ التعبير continue. يجب وضع التعبير continue في الشيفرة التي ستُنفَّذ في كل تكرار للحلقة، ويوضع عادةً ضمن تعبير if. سنستخدم نفس البرنامج الذي استعملناها لشرح التعبير break أعلاه، لكننا سنستخدم التعبير continue بدلًا من break: number = 0 for number in range(10): number = number + 1 if number == 5: continue # continue here print('Number is ' + str(number)) print('Out of loop') الفرق بين استخدام التعبير continue بدلًا من break هو إكمال تنفيذ الشيفرة بغض النظر عن التوقف الذي حدث عندما كانت قيمة المتغير number مساويةً إلى الرقم 5. لننظر إلى الناتج: Number is 1 Number is 2 Number is 3 Number is 4 Number is 6 Number is 7 Number is 8 Number is 9 Number is 10 Out of loop نلاحظ أنَّ السطر الذي يجب أن يحتوي على Number is 5 ليس موجودًا في المخرجات، لكن سيُكمَل تنفيذ حلقة التكرار بعد هذه المرحلة مما يطبع الأرقام من 6 إلى 10 قبل إنهاء تنفيذ الحلقة. يمكنك استخدام التعبير continue لتفادي استخدام تعابير شرطية معقدة ومتشعّبة، أو لتحسين أداء البرنامج عن طريق تجاهل الحالات التي ستُرفَض نتائجها. الخلاصة: التعبير continue سيؤدي إلى جعل البرنامج يتجاهل تنفيذ حلقة التكرار عند تحقيق شرط معين، لكن بعدئذٍ سيُكمِل تنفيذ الحلقة كالمعتاد. التعبير pass التعبير pass يسمح لنا بالتعامل مع أحد الشروط دون إيقاف عمل حلقة التكرار بأي شكل، أي ستُنفَّذ جميع التعابير البرمجية الموجودة في حلقة التكرار ما لم تستعمل تعابير مثل break أو continue فيها. وكما هو الحال مع التعابير السابقة، يجب وضع التعبير pass في الشيفرة التي ستُنفَّذ في كل تكرار للحلقة، ويوضع عادةً ضمن تعبير if. سنستخدم نفس البرنامج الذي استعملناها لشرح التعبير break أو continue أعلاه، لكننا سنستخدم التعبير pass هذه المرة: number = 0 for number in range(10): number = number + 1 if number == 5: pass # pass here print('Number is ' + str(number)) print('Out of loop') التعبير pass الذي يقع بعد العبارة الشرطية if يخبر البرنامج أنَّ عليه إكمال تنفيذ الحلقة وتجاهل مساواة المتغير number للرقم 5. لنشغِّل البرنامج ولننظر إلى الناتج: Number is 1 Number is 2 Number is 3 Number is 4 Number is 5 Number is 6 Number is 7 Number is 8 Number is 9 Number is 10 Out of loop لاحظنا عند استخدامنا للتعبير pass في هذا البرنامج أنَّ البرنامج يعمل كما لو أننا لم نضع عبارة شرطية داخل حلقة التكرار؛ حيث يخبر التعبير pass البرنامج أن يكمل التنفيذ كما لو أنَّ الشرط لم يتحقق. يمكن أن تستفيد من التعبير pass عندما تكتب برنامجك لأوّل مرة أثناء تفكيرك بحلّ مشكلة ما عبر خوارزمية، لكن قبل أن تضع التفاصيل التقنية له. الخلاصة تسمح لك التعابير break و continue و pass باستعمال حلقات for و while بطريقةٍ أكثر كفاءة. ترجمة –وبتصرّف– للمقال How To Use Break, Continue, and Pass Statements when Working with Loops in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضًا الدرس التالي: كيفية تعريف الدوال في بايثون 3 الدرس السابق: كيفية إنشاء حلقات تكرار for في بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  20. نستفيد من البرامج الحاسوبية خيرَ استفادة في أتمتة المهام وإجراء المهام التكرارية لكيلا نحتاج إلى القيام بها يدويًا، وإحدى طرائق تكرار المهام المتشابهة هي استخدام حلقات التكرار، وسنشرح في درسنا هذا حلقة تكرار while. حلفة تكرار while تؤدي إلى تكرار تنفيذ قسم من الشيفرة بناءً على متغير منطقي (boolean)، وسيستمر تنفيذ هذه الشيفرة لطالما كانت نتيجة التعبير المستعمل معها تساوي true. يمكنك أن تتخيل أنَّ حلقة while هي عبارة شريطة تكرارية، فبعد انتهاء تنفيذ العبارة الشرطية if فيُستَكمَل تنفيذ بقية البرنامج، لكن مع حلقة while فسيعود تنفيذ البرنامج إلى بداية الحلقة بعد انتهاء تنفيذها إلى أن يصبح الشرط مساويًا للقيمة false. وعلى النقيض من حلقات for التي تُنفَّذ عدد معيّن من المرات، فسيستمر تنفيذ حلقات while اعتمادًا على شرطٍ معيّن، لذا لن تحتاج إلى عدد مرات تنفيذ الحلقة قبل إنشائها. حلقة while الشكل العام لحلقات while في لغة بايثون كالآتي: while [a condition is True]: [do something] سيستمر تنفيذ التعليمات البرمجية الموجودة داخل الحلقة إلى أن يصبح الشرط false. لنُنشِئ برنامجًا صغيرًا فيه حلقة while، ففي هذه البرنامج سنطلب من المستخدم إدخال كلمة مرور. وهنالك خياران أمام حلقة التكرار: - إما أن تكون كلمة المرور صحيحة، فعندها سينتهي تنفيذ حلقة while. - أو أن تكون كلمة المرور غير صحيحة، فعندها سيستمر تنفيذ حلقة التكرار. لنُنشِئ ملفًا باسم password.py في محررنا النصي المفضَّل، ولنبدأ بتهيئة المتغير paasword بإسناد سلسلة نصية فارغة إليه: password = '' نستخدم المتغير السابق للحصول على مدخلات المستخدم داخل حلقة التكرار while. علينا بعد ذلك إنشاء حلقة while مع تحديد ما هو الشرط الذي يجب تحقيقه: password = '' while password != 'password': أتبَعنا –في المثال السابق– الكلمة المحجوزة while بالمتغير password، ثم سنتحقق إذا كانت قيمة المتغير password تساوي السلسلة النصية 'password' (لا تنسَ أنَّ قيمة المتغير سنحصل عليها من مدخلات المستخدم)، يمكنك أن تختار أي سلسلة نصية تشاء لمقارنة مدخلات المستخدم بها. هذا يعني أنَّه لو أدخل المستخدم السلسلة النصية password فستتوقف حلقة التكرار وسيُكمَل تنفيذ البرنامج وستُنفَّذ أيّة شيفرات خارج الحلقة، لكن إذا أدخل المستخدم أيّة سلسلة نصية لا تساوي password فسيُكمَل تنفيذ الحلقة. علينا بعد ذلك إضافة الشيفرة المسؤولة عمّا يحدث داخل حلقة while: password = '' while password != 'password': print('What is the password?') password = input() نفَّذ البرنامج عبارة print داخل حلقة while والتي تسأل المستخدم عن كلمة مروره، ثم أسندنا قيمة مدخلات المستخدم (التي حصلنا عليها عبر الدالة input()‎) إلى المتغير password. سيتحقق البرنامج إذا كانت قيمة المتغير password تساوي السلسلة النصية 'password'، وإذا تحقق ذلك فسينتهي تنفيذ حلقة while. لنضف سطرًا آخر إلى البرنامج لنعرف ماذا يحدث إن أصبحت قيمة الشرط مساويةً إلى false: password = '' while password != 'password': print('What is the password?') password = input() print('Yes, the password is ' + password + '. You may enter.') لاحظ أنَّ آخر عبارة print()‎ موجودة خارج حلقة while، لذا عندما يُدخِل المستخدم الكلمة password عند سؤاله عن كلمة مروره، فستُطبَع آخر جملة والتي تقع خارج حلقة التكرار. لكن ماذا يحدث لو لم يدخل المستخدم الكلمة password قط؟ حيث لن يستمر تنفيذ البرنامج ولن يروا آخر عبارة print()‎ وسيستمر تنفيذ حلقة التكرار إلى ما لا نهاية! يستمر تنفيذ حلقة التكرار إلى ما لا نهاية إذا بقي تنفيذ البرنامج داخل حلقة تكرار دون الخروج منها. وإذا أردتَ الخروج من حلقة تكرار نهائية، فاضغط Ctrl+C في سطر الأوامر. احفظ البرنامج ثم شغِّله: python password.py سيُطلَب منك إدخال كلمة المرور، ويمكنك تجربة ما تشاء من الكلمات. هذا مثالٌ عن ناتج البرنامج: What is the password? hello What is the password? sammy What is the password? PASSWORD What is the password? password Yes, the password is password. You may enter. أبقِ في ذهنك أنَّ السلاسل النصية حساسة لحالة الأحرف إلا إذا استعملتَ دالةً من دوال النصوص لتحويل السلسلة النصية إلى حالة الأحرف الصغيرة (على سبيل المثال) قبل التحقق منها. دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن مثال عن برنامج يستخدم حلقة while بعد أن تعلمنا المبدأ الأساسي لحلقة تكرار while، فلنُنشِئ لعبة تعمل على سطر الأوامر لتخمين الأرقام والتي تستعمل الحلقة while . نريد من الحاسوب أن يُنشِئ أرقامًا عشوائيةً لكي يحاول المستخدمون تخمينها، لذا علينا استيراد الوحدة random عبر استخدام العبارة import، وإذا لم تكن هذه الحزمة مألوفةً لك فيمكنك قراءة المزيد من المعلومات عن توليد الأرقام العشوائية في توثيق بايثون. لنُنشِئ بدايةً ملفًا باسم guess.py في محررك النصي المفضَّل: import random علينا الآن إسناد عدد صحيح عشوائي إلى المتغير number، ولنجعل مجاله من 1 إلى 25 (بما فيها تلك الأرقام) كيلا نجعل اللعبة صعبة جدًا. import random number = random.randint(1, 25) يمكننا الآن إنشاء حلقة while، وذلك بتهيئة متغير ثم كتابة الحلقة: import random number = random.randint(1, 25) number_of_guesses = 0 while number_of_guesses < 5: print('Guess a number between 1 and 25:') guess = input() guess = int(guess) number_of_guesses = number_of_guesses + 1 if guess == number: break هيئنا متغيرًا اسمه number_of_guesses قيمته 0، وسوف نزيد قيمته عند كل تكرار للحلقة لكي لا تصبح حلقتنا لا نهائية. ثم سنضيف تعبير while الذي يشترط ألّا تزيد قيمة المتغير number_of_guesses عن 5. وبعد المحاولة الخامسة سيُعاد المستخدم إلى سطر الأوامر، وإذا حاول المستخدم إدخال أيّ شيء غير رقمي فسيحصل على رسالة خطأ. أضفنا داخل حلقة while عبارة print()‎ لطلب إدخال رقم من المستخدم، ثم سنأخذ مدخلات المستخدم عبر الدالة input()‎ ونُسنِدَها إلى المتغير guess، ثم سنحوِّل المتغير guess من سلسلة نصية إلى عدد صحيح. وقبل انتهاء حلقة التكرار، فعلينا زيادة قيمة المتغير number_of_guesses بمقدار 1، لكيلا تُنفَّذ حلقة التكرار أكثر من 5 مرات. وفي النهاية، كتبنا عبارة if شرطية لنرى إذا كان المتغير guess الذي أدخله المستخدم مساوٍ للرقم الموجود في المتغير number الذي ولَّده الحاسوب، وإذا تحقق الشرط فسنستخدم عبارة break للخروج من الحلقة. أصبح البرنامج جاهزًا للاستخدام، ويمكننا تشغيله عبر تنفيذ الأمر: python guess.py صحيحٌ أنَّ البرنامج يعمل عملًا سليمًا، لكن المستخدم لن يعلم إذا كان تخمينه صحيحًا ويمكنه أن يخمِّن الرقم خمس مرات دون أن يعلم إذا كانت إحدى محاولاته صحيحة. هذا مثال عن مخرجات البرنامج: Guess a number between 1 and 25: 11 Guess a number between 1 and 25: 19 Guess a number between 1 and 25: 22 Guess a number between 1 and 25: 3 Guess a number between 1 and 25: 8 لنضف بعض العبارات الشرطية خارج حلقة التكرار لكي يحصل المستخدم على معلومات فيما إذا استطاعوا تخمين الرقم أم لا، وسنضيف هذه العبارات في نهاية الملف: import random number = random.randint(1, 25) number_of_guesses = 0 while number_of_guesses < 5: print('Guess a number between 1 and 25:') guess = input() guess = int(guess) number_of_guesses = number_of_guesses + 1 if guess == number: break if guess == number: print('You guessed the number in ' + str(number_of_guesses) + ' tries!') else: print('You did not guess the number. The number was ' + str(number)) في هذه المرحلة سيُخبِر البرنامجُ المستخدمَ إذا استطاعوا تخمين الرقم، لكن ذلك لن يحدث إلا بعد انتهاء حلقة التكرار وبعد انتهاء عدد مرات التخمين المسموحة. ولمساعد المستخدم قليلًا، فلنضف بعض العبارات الشرطية داخل حلقة while وتلك العبارات ستخبر المستخدم إذا كان تخمينه أعلى من الرقم أو أصغر منه، لكي يستطيعوا تخمين الرقم بنجاح، وسنضيف تلك العبارات الشرطية قبل السطر الذي يحتوي على if guess == number: import random number = random.randint(1, 25) number_of_guesses = 0 while number_of_guesses < 5: print('Guess a number between 1 and 25:') guess = input() guess = int(guess) number_of_guesses = number_of_guesses + 1 if guess < number: print('Your guess is too low') if guess > number: print('Your guess is too high') if guess == number: break if guess == number: print('You guessed the number in ' + str(number_of_guesses) + ' tries!') else: print('You did not guess the number. The number was ' + str(number)) وعندما نُشغِّل البرنامج مرةً أخرى بتنفيذ python guess.py، فيمكننا ملاحظة أنَّ المستخدم سيحصل على بعض المساعدة، فلو كان الرقم المولَّد عشوائيًا هو 12 وكان تخمين المستخدم 18، فسيُخبره البرنامج أنَّ الرقم الذي خمنه أكبر من الرقم العشوائي، وذلك لكي يستطيع تعديل تخمنيه وفقًا لذلك. هنالك الكثير من التحسينات التي يمكن إجراؤها على الشيفرة السابقة، مثل تضمين آلية لمعالجة الأخطاء التي تحدث عندما لا يُدخِل المستخدم عددًا صحيحًا، لكن كان غرضنا هو رؤية كيفية استخدام حلقة while في برنامج قصير ومفيد يعمل من سطر الأوامر. الخلاصة شرحنا في هذا الدرس كيف تعمل حلقات while في بايثون وكيفية إنشائها. حيث تستمر حلقات while بتنفيذ مجموعة من الأسطر البرمجية لطالما كان الشرط مساويًا للقيمة true. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة –وبتصرّف– للمقال How To Construct While Loops in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضًا الدرس التالي: كيفية إنشاء حلقات تكرار for في بايثون 3 الدرس السابق: كيفية كتابة التعليمات الشرطية في بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  21. لا تخلو لغة برمجة من التعليمات الشرطية التي تُنفَّذ بناءً على تحقق شرط معين، وهي تعليمات برمجية يمكنها التحكم في تنفيذ شفرات معينة بحسب تحقق شرط ما من عدمه في وقت التنفيذ. تُنفّذ تعليمات برامج بايثون من الأعلى إلى الأسفل، مع تنفيذ كل سطر بحسب ترتيبه. باستخدام التعليمات الشرطية، يمكن للبرامج التحقق من استيفاء شروط معينة، ومن ثم تنفيذ الشيفرة المقابلة. هذه بعض الأمثلة التي سنستخدم فيها التعليمات الشرطية: إن حصلت الطالبة على أكثر من 65٪ في الامتحان، فأعلن عن نجاحها؛ وإلا، فأعلن عن رسوبها إذا كان لديه مال في حسابه، فاحسب الفائدة. وإلا، فاحسب غرامة إن اشتروا 10 برتقالات أو أكثر، فاحسب خصمًا بمقدار 5٪؛ وإلا فلا تفعل تقيِّم الشيفرة الشرطية شروطًا، ثم تُنفِّذ شيفرةً بناءً على ما إذا تحققت تلك الشروط أم لا. ستتعلم في هذا الدرس كيفية كتابة التعليمات الشرطية في بايثون. التعليمة if سنبدأ بالتعليمة ‎if‎، والتي تتحقق مما إذا تحقق شرط محدَّد أم لا، وفي حال تحقق الشرط، فستنفَّذ الشيفرة المقابلة له. لنبدأ بأمثلة عملية توضح ذلك. افتح ملفًا، واكتب الشيفرة التالية: grade = 70 if grade >= 65: print("درجة النجاح") أعطينا للمتغير ‎grade‎ القيمة ‎70‎. ثم استخدمنا التعليمة ‎if‎ لتقييم ما إذا كان المتغير grade أكبر من (‎>=‎) أو يساوي ‎65‎. وفي تلك الحالة، سيطبع البرنامج السلسلة النصية التالية: درجة النجاح. احفظ البرنامج بالاسم ‎grade.py‎، ثم نفّذه في بيئة البرمجة المحلية من نافذة الطرفية باستخدام الأمر ‎python grade.py‎. في هذه الحالة، الدرجة 70 تلبي الشرط، لأنّها أكبر من 65، لذلك ستحصل على المخرجات التالية عند تنفيذ البرنامج: درجة النجاح لنغيّر الآن نتيجة هذا البرنامج عبر تغيير قيمة المتغير ‎grade‎ إلى ‎60‎: grade = 60 if grade >= 65: print("درجة النجاح") بعد حفظ وتنفيذ الشيفرة، لن نحصل على أي مخرجات، لأنّ الشرط لم يتحقق، ولم نأمر البرنامج بتنفيذ تعليمة أخرى. كمثال آخر، دعنا نتحقق مما إذا كان رصيد الحساب المصرفي أقل من 0. لننشئ ملفا باسم ‎account.py‎، ونكتب البرنامج التالي: balance = -5 if balance < 0: print("الحساب فارغ، أضف مبلغا الآن، أو ستحصل على غرامة.") عند تنفيذ البرنامج باستخدام ‎python account.py‎، سنحصل على المخرجات التالية: الحساب فارغ، أضف مبلغًا الآن، أو ستحصل على غرامة. أعطينا للمتغير ‎balance‎ القيمة ‎-5‎، وهي أقل من 0 في البرنامج السابق. ولمَّا كان الرصيد مستوفيًا لشرط التعليمة ‎if‎ (أي ‎balance < 0‎)، فسنحصل على سلسلة نصية في المخرجات بمجرد حفظ الشيفرة وتنفيذها. مرة أخرى، لو غيرنا الرصيد إلى القيمة 0 أو إلى عدد موجب، فلن نحصل على أيّ مخرجات. دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن التعليمة Else قد تريد من البرنامج أن يفعل شيئًا ما في حال عدم تحقق شرط التعليمة ‎if‎. في المثال أعلاه، نريد طباعة مخرجات في حال النجاح والرسوب. ولفعل ذلك، سنضيف التعليمة ‎else‎ إلى شرط الدرجة أعلاه وفق الصياغة التالية: grade = 60 if grade >= 65: print("درجة النجاح") else: print("درجة الرسوب") قيمة المتغير ‎‎grade‎‎ تساوي ‎60‎، لذلك فشرط التعليمة ‎if‎ غير متحقق، وبالتالي فإنّ البرنامج لن يطبع ‎درجة النجاح‎. تخبر التعليمة ‎else‎ البرنامجَ أنّ عليه طباعة السلسلة النصية "درجة الرسوب". عندما نحفظ البرنامج وننفّذه، سنحصل على المخرجات التالية: درجة الرسوب إذا عدّلنا البرنامج وأعطينا المتغيرَ grade القيمة ‎65‎ أو أعلى منها، فسنحصل بدلاً من ذلك على الناتج ‎درجة النجاح‎. لإضافة التعليمة ‎else‎ إلى مثال الحساب المصرفي، سنعيد كتابة الشيفرة كما يلي: balance = 522 if balance < 0: print("الحساب فارغ، أضف مبلغا الآن، أو ستحصل على غرامة.") else: print("رصيدك أكبر من 0.") سنحصل على المخرجات التالية: رصيدك أكبر من 0. هنا، غيّرنا قيمة المتغير ‎balance‎ إلى عدد موجب لكي تُنفَّذ الشيفرة المقابلة للتعليمة ‎else‎. إن أردت تنفيذ الشيفرة المقابلة للتعليمة ‎if‎، غيِّر القيمة إلى عدد سالب. من خلال دمج العبارتين ‎if‎ و ‎else‎، فأنت تنشئ تعليمة شرطية مزدوجة، والتي ستجعل الحاسوب ينفذ شيفرة برمجية معينة سواء تم استيفاء شرط ‎if‎ أم لا. التعليمة Else if حتى الآن، عملنا على تعليمات شرطية ثنائية، أي إن تحقق الشرط، فنفذ شيفرة ما، وإلا، فنفِّذ شيفرة أخرى فقط. لكن في بعض الحالات، قد تريد برنامجًا يتحقق من عدة حالات شرطية. ولأجل هذا، سوف نستخدم التعليمة Else if، والتي تُكتب في بايثون هكذا ‎elif‎. تشبه التعليمة ‎elif‎ - أو Else if - التعليمة ‎if‎، ومهمتها التحقق من شرط إضافي. في برنامج الحساب المصرفي، قد نرغب في الحصول على ثلاثة مخرجات مختلفة مقابلة لثلاث حالات مختلفة: الرصيد أقل من 0 الرصيد يساوي 0 الرصيد أعلى من 0 ستوضع التعليمة ‎elif‎ بين التعليمة ‎if‎ والتعليمة ‎else‎ كما يلي: . . . if balance < 0: print("الحساب فارغ، أضف مبلغا الآن، أو ستحصل على غرامة.") elif balance == 0: print("الرصيد يساوي 0، أضف مبلغًا قريبًا.") else: print("رصيدك أكبر من 0.") الآن، هناك ثلاثة مخرجات محتملة يمكن أن تُطبع عند تنفيذ البرنامج: إن كان المتغير ‎balance‎ يساوي ‎0‎، فسنحصل على المخرجات من التعليمة ‎elif‎ (أي ‎الرصيد يساوي 0، أضف مبلغًا قريبًا.‎). إذا ضُبِط المتغير ‎balance‎ عند عدد موجب، فسوف نحصل على المخرجات من التعليمة ‎else‎ (أي ‎رصيدك أكبر من 0.‎). إذا ضُبِط المتغير ‎balance‎ عند عدد سالب، فسنحصل على المخرجات من التعليمة ‎if‎ (أي ‎الحساب فارغ، أضف مبلغا الآن، أو ستحصل على غرامة‎). ماذا لو أردنا أن نأخذ بالحسبان أكثر من ثلاثة احتمالات؟ يمكننا كتابة عدة تعليمات ‎elif‎ في الشيفرة البرمجية. لنُعِد كتابة البرنامج ‎grade.py‎ بحيث يقابل كل نطاق من الدرجات علامة محددة: 90 أو أعلى تكافئ العلامة أ 80-89 تعادل العلامة ب 70-79 تعادل العلامة ج 65-69 تعادل العلامة د 64 أو أقل تكافئ العلامة ه سنحتاج لتنفيذ هذه الشيفرة إلى تعليمة ‎if‎ واحد، وثلاث تعليمات ‎elif‎، وتعليمة ‎else‎ تعالج جميع الحالات الأخرى. دعنا نعيد كتابة الشيفرة من المثال أعلاه لطباعة سلسلة نصية مقابلة لكل علامة. يمكننا الإبقاء على التعليمة ‎else‎ كما هي. . . . if grade >= 90: print("العلامة أ") elif grade >=80: print("العلامة ب") elif grade >=70: print("العلامة ج") elif grade >= 65: print("العلامة د") else: print("درجة الرسوب") تُنفّذ التعليمات ‎elif‎ بالترتيب. هذا البرنامج سيكمل الخطوات التالية: إذا كانت الدرجة أكبر من 90، فسيطبع البرنامجُ ‎العلامة أ‎، وإذا كانت الدرجة أقل من 90، فسيمرّ البرنامج إلى التعليمة التالية … إذا كانت الدرجة أكبر من أو تساوي 80، فسيطبع البرنامجُ ‎العلامة ب‎، إذا كانت الدرجة تساوي 79 أو أقل، فسيمرّ البرنامج إلى التعليمة التالية … إذا كانت الدرجة أكبر من أو تساوي 70، فسيطبعُ البرنامجُ ‎العلامة ج‎، إذا كانت الدرجة تساوي 69 أو أقل، فسيمرّ البرنامج إلى التعليمة التالية … إذا كانت الدرجة أكبر من أو تساوي 65، فسيطبع البرنامجُ ‎العلامة د‎، وإذا كانت الدرجة تساوي 64 أو أقل، فسيمرّ البرنامج إلى التعليمة التالية … سيطبع البرنامج ‎درجة الرسوب‎، لأنه لم يتم استيفاء أيِّ من الشروط المذكورة أعلاه. تعليمات if المتشعبة بعد أن تتعود على التعليمات ‎if‎ و ‎elif‎ و ‎else‎، يمكنك الانتقال إلى التعليمات الشرطية المتشعبة (nested conditional statements). يمكننا استخدام تعليمات ‎if‎ المتشعبة في الحالات التي نريد فيها التحقق من شرط ثانوي بعد التأكد من تحقق الشرط الرئيسي. لهذا، يمكننا حشر تعليمة if-else داخل تعليمة if-else أخرى. لنلقِ نظرة على صياغة ‎if‎ المتشعبة: if statement1: # الخارجية if تعليمة print("true") if nested_statement: # المتشعبة if تعليمة print("yes") else: # المتشعبة else تعليمة print("no") else: # الخارجية else تعليمة print("false") هناك عدة مخرجات محتملة لهذه الشيفرة: إذا كانت ‎statement1‎ صحيحة، فسيتحقق البرنامج مما إذا كانت ‎nested_statement‎ صحيحة أيضًا. إذا كانت كلتا الحالتين صحيحتان، فسنحصل على المخرجات التالية: true yes ولكن إن كانت ‎statement1‎ صحيحة، و ‎nested_statement‎ خاطئة، فسنحصل على المخرجات التالية: true no وإذا كانت ‎statement1‎ خاطئة، فلن تُنفّذ تعليمة if-else المتشعبة على أيّ حال، لذلك ستُنفّذ التعليمة ‎else‎ وحدها، والمخرجات ستكون: false يمكن أيضًا استخدام عدة تعليمات ‎if‎ متشعبة في الشيفرة: if statement1: # الخارجية if print("hello world") if nested_statement1: # المتشعبة الأولى if print("yes") elif nested_statement2: # المتشعبة الأولى elif print("maybe") else: #المتشعبة الأولى else print("no") elif statement2: # الخارجية elif print("hello galaxy") if nested_statement3: # المتشعبة الثانية if print("yes") elif nested_statement4: # المتشعبة الثانية elif print("maybe") else: # المتشعبة الثانية else print("no") else: # الخارجية else statement("مرحبا") في الشيفرة البرمجية أعلاه، هناك تعليمات ‎if‎ و ‎elif‎ متشعبة داخل كل تعليمات ‎if‎. هذا سيفسح المجال لمزيد من الخيارات في كل حالة. دعنا نلقي نظرة على مثال لتعليمات ‎if‎ متشعبة في البرنامج ‎grade.py‎. يمكننا التحقق أولًا مما إذا كان الطالب قد حقق درجة النجاح (أكبر من أو تساوي 65٪)، ثم نحدد العلامة المقابلة للدرجة. إذا لم يحقق الطالب درجة النجاح، فلا داعي للبحث عن العلامة المقابلة للدرجة، وبدلًا من ذلك، يمكن أن نجعل البرنامج يطبع سلسلة نصية فيها إعلان عن رسوب الطالب. ستبدو الشيفرة المعدلة كما يلي: . . . if grade >= 65: print("درجة النجاح:") if grade >= 90: print("أ") elif grade >=80: print("ب") elif grade >=70: print("ج") elif grade >= 65: print("د") else: print("درجة الرسوب") إذا أعطينا للمتغير ‎grade‎ القيمة ‎92‎، فسيُستوفى الشرط الأول، وسيَطبع البرنامجُ ‎درجة النجاح:‎. بعد ذلك، سيتحقق مما إذا كانت الدرجة أكبر من أو تساوي 90، وبما أنّ هذا الشرط مستوفًى أيضًا، فستُطبع ‎أ‎. أما إذا أعطينا للمتغير ‎grade‎ القيمة ‎60‎، فلن يتم استيفاء الشرط الأول، لذلك سيتخطى البرنامج تعليمات ‎if‎ المتشعبة، وينتقل إلى التعليمة ‎else‎، ويطبع ‎درجة الرسوب‎. يمكننا بالطبع إضافة المزيد من الخيارات، واستخدام طبقة ثانية من تعليمات if المتشعبة. ربما نود إضافة الدرجات التفصيلية أ+‎ و أ و أ-‎. يمكننا القيام بذلك عن طريق التحقق أولًا من اجتياز درجة النجاح، ثم التحقق مما إذا كانت الدرجة تساوي 90 أو أعلى، ثم التحقق مما إذا كانت الدرجة تتجاوز 96، وفي تلك الحالة ستقابل العلامة ‎أ+. إليك المثال التالي: . . . if grade >= 65: print("درجة النجاح:") if grade >= 90: if grade > 96: print("أ+") elif grade > 93 and grade <= 96: print("أ") elif grade >= 90: print("أ-") . . . في الشيفرة أعلاه، في حال تعيين المتغير ‎grade‎ عند القيمة ‎96‎، سيقوم البرنامج بما يلي: التحقق مما إذا كانت الدرجة أكبر من أو تساوي 65 (صحيح) طباعة ‎درجة النجاح:‎ التحقق مما إذا كانت الدرجة أكبر من أو تساوي 90 (صحيح) التحقق مما إذا كانت الدرجة أكبر من 96 (خطأ) التحقق مما إذا كانت الدرجة أكبر من 93، وأقل من أو تساوي 96 (صحيح) طباعة أ‎ تجاوز التعليمات الشرطية المتشعبة وتنفيذ باقي الشيفرة ستكون مخرجات البرنامج في حال كانت الدرجة تساوي 96 كالتالي: درجة النجاح: أ تساعد تعليمات ‎if‎ المتشعبة على إضافة عدة مستويات من الشروط الفرعية إلى الشيفرة. خلاصة ستتحكم باستخدام التعليمات الشرطية، مثل التعليمة ‎if‎، في مسار البرنامج أي تدفق تنفيذ الشيفرة. تطلب التعليمات الشرطية من البرنامج التحقق من استيفاء شرط معين من عدمه. وإذا تم استيفاء الشرط، فستُنفّذ شيفرة معينة، وإلا فسيستمر البرنامج وينتقل إلى الأسطر التالية. يمكنك الدمج بين التعليمات الشرطية والمعاملات المنطقية، بما فيها and و or، واستخدام التعليمات الشرطية مع الحلقات التكرارية. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة -وبتصرّف- للمقال How To Write Conditional Statements in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة التالية: كيفية إنشاء حلقات تكرار while في بايثون 3 المقالة السابقة: كيفية كتابة الوحدات في بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  22. وحدات (modules) بايثون هي ملفات ‎.py‎ تحتوي شفرة بايثون، ويمكن التعامل مع أيّ ملف بايثون على أنه وحدة. تتوفر بعض الوحدات في مكتبة بايثون القياسية، والتي تُثبّت تلقائيًا مع بايثون. يمكن لبعضها الآخر أن يُثبّت عبر مدير الحزم pip. بالإضافة إلى ذلك، يمكنك إنشاء وحدات بايثون خاصة بك، لأنّ الوحدات هي مجرد ملفات ‎.py‎. سيرشدك هذا الدرس إلى كيفية كتابة وحدات بايثون لاستخدامها في ملفات البرمجة الأخرى. كتابة الوحدات واستيرادها كتابة الوحدات مشابه لكتابة أي ملف بايثون آخر. يمكن أن تحتوي الوحدات على تعريفات الدوال والأصناف والمتغيرات التي يمكن استخدامها بعد ذلك في برامج بايثون الأخرى. سنشئ من بيئة البرمجة الحالية الخاصة ببايثون 3 أو بيئة البرمجة المستندة إلى الخادم ملفًّا باسم ‎hello.py‎، والذي سنتستورده لاحقًا من ملف آخر. في البدء، سننشئ دالة تطبع العبارة مرحبا بالعالم!: # تعريف دالة def world(): print("مرحبا بالعالم!") إذا نفّذنا البرنامج في سطر الأوامر باستخدام ‎python hello.py‎، فلن يحدث شيء، لأننا لم نطلب من البرنامج فعل أي شيء. لننشئ ملفًا ثانيًا في نفس المجلد (أي بجانب الملف السابق) باسم ‎main_program.py‎ حتى نتمكن من استيراد الوحدة التي أنشأناها للتو، ومن ثم استدعاء الدالة. يجب أن يكون هذا الملف في نفس المجلد حتى تعرف بايثون موضع الوحدة، لأنها ليست وحدة مُضمّنة. # hello استيراد الوحدة import hello # استدعاء الدالة hello.world() نظرًا لأننا استوردنا الوحدة، نحتاج إلى استدعاء الدالة من خلال التأشير إلى اسم الوحدة بالصياغة النقطية (dot notation). يمكننا بدلًا من ذلك استيراد دالة محدَّدة من الوحدة بالتعليمة ‎from hello import world‎، واستدعاء تلك الدالة بالشكل ‎world()‎ كما تعلمنا ذلك من الدرس السابق. الآن، يمكننا تنفيذ البرنامج من سطر الأوامر: python main_program.py سنحصل على المخرجات التالية: مرحبا بالعالم! لنرى كيف يمكننا استخدام المتغيرات في الوحدات، دعنا نضيف تعريفًا لمتغير في الملف ‎hello.py‎: # تعريف دالة def world(): print("مرحبا بالعالم!") # تعريف المتغير shark = "Sammy" بعد ذلك، سنستدعي المتغير داخل الدالة ‎print()‎ في الملف ‎main_program.py‎: # hello استيراد الوحدة import hello # استدعاء الدالة hello.world() # طباعة المتغير print(hello.shark) بمجرد تنفيذ البرنامج، سنحصل على المخرجات التالية: مرحبا بالعالم! Sammy أخيرًا، دعنا نعرّف صنفًا في الملف ‎hello.py‎. سننشئ الصنف ‎Octopus‎، والذي يحتوي على الخاصيتين ‎name‎ و ‎color‎، إضافة إلى دالة تطبع الخاصيات عند استدعائها. # تعريف الدالة def world(): print("مرحبا بالعالم!") # تعريف المتغير shark = "Sammy" # تعريف الصنف class Octopus: def __init__(self, name, color): self.color = color self.name = name def tell_me_about_the_octopus(self): print("This octopus is " + self.color + ".") print(self.name + " is the octopus's name.") سنضيف الآن الصنفَ إلى نهاية الملف ‎main_program.py‎: # hello استيراد الوحدة import hello # استدعاء الدالة hello.world() # طباعة المتغير print(hello.shark) # استدعاء الصنف jesse = hello.Octopus("Jesse", "orange") jesse.tell_me_about_the_octopus() بمجرد استدعاء الصنف Octopus باستخدام ‎hello.Octopus()‎، يمكننا الوصول إلى دوال وخاصيات الصنف من فضاء الأسماء الخاص بالملف ‎main_program.py‎. يتيح لنا هذا كتابة ‎jesse.tell_me_about_the_octopus()‎ في السطر الأخير دون استدعاء ‎hello‎. يمكننا أيضًا، على سبيل المثال، استدعاء إحدى خاصيات الصنف، مثل ‎jesse.color‎، دون الرجوع إلى اسم الوحدة ‎hello‎. سنحصل عند تنفيذ البرنامج على المخرجات التالية: مرحبا بالعالم! Sammy This octopus is orange. Jesse is the octopus's name. من المهم أن تضع في الحسبان أنه على الرغم من أنّ الوحدات غالبًا ما تضم تعريفات، إلا أنها يمكن أيضًا أن تقدم شفرات برمجية. لتوضيح هذا، دعنا نعيد كتابة الملف ‎hello.py‎ لنجعله يقدم دالة ‎world()‎: # تعريف دالة def world(): print("مرحبا بالعالم!") # استدعاء الدالة داخل الوحدة world() لقد حذفنا أيضًا التعريفات الأخرى في الملف. الآن، في الملف ‎main_program.py‎، سنحذف كل الأسطر باستثناء عبارة الاستيراد: # hello استيراد الوحدة import hello عند تنفيذ ‎main_program.py‎، سنحصل على المخرجات التالية: مرحبا بالعالم! هذا لأنّ الوحدة ‎hello‎ قدمت الدالة ‎world()‎، والتي مُرِّرت بعد ذلك إلى ‎main_program.py‎ لتُنفّذ مع السكربت ‎main_program.py‎. الوحدة هي ملف بايثون مؤلف من تعريفات و شيفرات برمجية يمكن الاستفادة منها في ملفات بايثون الأخرى. الوصول إلى الوحدات من مجلد آخر قد تكون الوحدات مفيدة لأكثر من مشروع واحد، وفي هذه الحالة، لن يكون من الحكمة الاحتفاظ بالوحدة في مجلد مرتبط بمشروع خاص. إذا أردت استخدام وحدة من مجلد آخر غير المجلد الذي يحوي البرنامج الرئيسي، فأمامك عدة خيارات سنسردها فيما يلي. التعرف تلقائيًا على مسار الوحدة أحد الخيارات هو استدعاء مسار الوحدة من الملفات البرمجية التي تستخدم تلك الوحدة. يُعد هذا حلًّا مؤقتًا يمكن استخدامه أثناء عملية التطوير، لأنه لا يجعل الوحدة متاحة على مستوى النظام بأكمله. لإلحاق مسار وحدة بملف برمجي آخر، ستبدأ باستيراد الوحدة ‎sys‎، إلى جانب الوحدات الأخرى التي ترغب في استخدامها في ملف البرنامج الرئيسي. تعد الوحدة ‎sys‎ جزءًا من مكتبة بايثون القياسية، وتوفر معاملات ودوال نظامية يمكنك استخدامها في برنامجك لتعيين مسار الوحدة التي ترغب في تقديمها. على سبيل المثال، لنقل أننا نقلنا الملف ‎hello.py‎ إلى المسار ‎/usr/sammy/‎، بينما يوجد الملف ‎main_program.py‎ في مجلد آخر. في الملف ‎main_program.py‎، ما يزال بإمكاننا استيراد الوحدة ‎hello‎ عن طريق استيراد الوحدة ‎sys‎، ثم إضافة المسار ‎/usr/sammy/‎ إلى المسارات التي يبحث بايثون فيها عن الملفات. import sys sys.path.append('/usr/sammy/') import hello ... إن عيّنت مسار الملف ‎hello.py‎ بشكل صحيح، فسيكون بمقدورك تنفيذ الملف ‎main_program.py‎ دون أيّ أخطاء، وستحصل على نفس المخرجات التي حصلنا عليها أعلاه عندما كان ‎hello.py‎ في نفس المجلد. إضافة الوحدة إلى مسار بايثون الخيار الثاني هو إضافة الوحدة إلى المسار الذي يبحث فيه بايثون عن الوحدات والحزم. هذا حل أفضل وأدوم، لأنه يجعل الوحدة متاحة على نطاق البيئة، أو على مستوى النظام. لمعرفة المسار الذي يبحث فيه بايثون، شغِّل مترجم (interpreter) بايثون من بيئة البرمجة خاصتك: python بعد ذلك، استورد الوحدة ‎sys‎: import sys ثم اطلب من بايثون طباعة مسار النظام: print(sys.path) ستحصل على بعض المخرجات، وسيُطبع مسار نظام واحد على الأقل. إذا كنت تعمل في بيئة برمجة، فقد تتلقى العديد منها. سيكون عليك البحث عن المسارات الموجودة في البيئة التي تستخدمها حاليًا، ولكن قد ترغب أيضًا في إضافة الوحدة إلى مسار النظام الرئيسي لبايثون. النتيجة ستكون مشابهة لما يلي: '/usr/sammy/my_env/lib/python3.5/site-packages' يمكنك الآن نقل الملف ‎hello.py‎ إلى هذا المجلد. بعد ذلك، يمكنك استيراد الوحدة ‎hello‎ كالمعتاد: import hello ... عند تنفيذ البرنامج، يُفترض ألا يحدث أيّ خطأ. يضمن لك تعديل مسار الوحدة إمكانية الوصول إليها مهما كان المجلد الذي تعمل فيه. هذا مفيد خاصة في حال كنت تعمل على عدة مشاريع تشير إلى الوحدة نفسها. خلاصة إنّ كتابة وحدات بايثون لا يختلف عن كتابة أيّ ملف بايثون آخر. غطينا في هذه المقالة كيفية كتابة التعاريف في الوحدات، وكيفية استخدامها في ملف بايثون آخر، وعرضنا بعض الخيارات حول المواضِع التي يمكن أن تحفظ فيها الوحدة لتجعلها متاحة. يمكنك تعلم المزيد حول تثبيت الوحدات واستيرادها من الدرس السابق: [كيفية استيراد الوحدات في بايثون 3](). هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة -وبتصرّف- للمقال How To Write Modules in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة التالية: كيفية كتابة العبارات الشرطية في بايثون 3 المقالة السابقة: كيفية استيراد الوحدات في بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  23. توفر لغة بايثون مجموعة متنوعة من الدوال المضمّنة مثل: print()‎‎: تطبع التعابير المُمرَّرة إليها في مجرى الخرج abs()‎‎: تُعيد القيمة المطلقة للعدد int()‎‎: تحوِّل نوع بيانات إلى عدد صحيح len()‎‎: تُعيد طول تسلسل أو مجموعة هذه الدوال المضمّنة وغيرها مفيدة، لكنها محدودة، لهذا يستخدم المطورون الوحدات (modules) لتطوير برامج أكثر تعقيدًا. الوحدات (Modules) هي ملفات بايثون ذات امتداد ‎.py، والتي تحوي شيفرات بايثون. يمكن التعامل مع أيّ ملف بايثون على أنه وحدة. مثلًا، إن كان هناك ملف بايثون يسمى hello.py، فسيكون اسم الوحدة المقابلة له hello، والذي يمكن استيراده في ملفات بايثون الأخرى، أو استخدامه في مترجم (interpreter) سطر أوامر بايثون. ستتعلم في المقالة التالية كيفية إنشاء الوحدات. يمكن للوحدات أن تعرِّف دوالًا وأصنافًا ومتغيرات يمكن الرجوع إليها من ملفات بايثون الأخرى، أو من مترجم سطر أوامر بايثون. في بايثون، يمكنك الوصول إلى الوحدات باستخدام العبارة import. عند فعل ذلك، ستُنفَّذ شيفرة الوحدة، مع الاحتفاظ بنطاقات (scopes) التعريفات حتى تكون متاحة في ملفك الحالي. عندما تستورد بايثون وحدةً باسم hello على سبيل المثال، فسيبحث المترجم أولًا عن وحدة مضمّنة باسم hello. فإن لم يجد، فسيبحث عن ملف يسمى hello.py في قائمة من المجلدات يحددها المتغير sys.path. سيرشدك هذا الدرس إلى كيفية البحث عن الوحدات وتثبيتها، واستيرادها، وإعادة تسميتها (aliasing). تثبيت الوحدات هناك عدد من الوحدات المضمنة في مكتبة بايثون القياسية التي تحتوي على العديد من الوحدات التي توفر الكثير من وظائف النظام، أو توفر حلولًا قياسية. مكتبة بايثون القياسية تأتي مع كل توزيعات بايثون. للتحقق من أنّ وحدات بايثون جاهزة للعمل، ادخل إلى بيئة برمجة بايثون 3 المحلية، أو بيئة البرمجة المستندة إلى الخادم، وشغّل مترجم بايثون في سطر الأوامر على النحو التالي: (my_env) sammy@ubuntu:~/environment$ python من داخل المترجم، يمكنك تنفيذ العبارة import مع اسم الوحدة للتأكد من أنّها جاهزة: import math لمّا كانت math وحدة مضمّنة، فينبغي أن يُكمل المترجم المهمة دون أي مشاكل، ثم يعود إلى المحث (prompt). هذا يعني أنك لست بحاجة إلى فعل أيّ شيء للبدء في استخدام الوحدة math. لننفِّذ الآن العبارة import مع وحدة قد لا تكون مُثبّتة عندك، مثل matplotlib، وهي مكتبة للرسم ثنائي الأبعاد: import matplotlib إذا لم تكن matplotlib مثبتة، فستتلقّى خطأً مثل هذا: ImportError: No module named 'matplotlib' يمكنك إيقاف مترجم بايثون بالضغط على CTRL + D، ثم تثبيت الوحدة matplotlib عبر pip بتنفيذ الأمر التالي: (my_env) sammy@ubuntu:~/environment$ pip install matplotlib بمجرد تثبيتها، يمكنك استيراد matplotlib من مترجم بايثون باستخدام import matplotlib، ولن يحدث أيّ خطأ. استيراد الوحدات للاستفادة من الدوال الموجودة في الوحدة، ستحتاج إلى استيراد الوحدة عبر التعليمة import. تتألف التعليمة import من الكلمة المفتاحية import معقوبة باسم الوحدة. يُصرَّح عن عملية استيراد الوحدات في أعلى ملفات بايثون، قبل الأسطر التوجيهية (shebang lines أي الأسطر التي تبدأ بـ ‎#!‎)، أو التعليقات العامة. لذلك، سنستورد في ملف برنامج بايثون my_rand_int.py الوحدة random لتوليد أعداد عشوائية على النحو التالي: import random عندما نستورد وحدة، فإننا نجعلها متاحة في برنامجنا الحالي كفضاء أسماء (namespace) منفصل. هذا يعني أنه سيتعيّن علينا الرجوع إلى الدالة باستخدام الصياغة النقطية (dot notation) على النحو التالي [module].[function]. عمليًا، باستخدام مثال الوحدة random، ستبدو الشفرة كما يلي: random.randint()‎‎‎: تستدعي الدالة لإعادة عدد صحيح عشوائي، أو random.randrange()‎‎: تستدعي الدالة لإعادة عنصر عشوائي من نطاق محدد. دعنا ننشئ حلقة for لتوضيح كيفية استدعاء دالة من الوحدة random ضمن البرنامج my_rand_int.py: import random for i in range(10): print(random.randint(1, 25)) يستورد هذا البرنامج الصغير الوحدة random في السطر الأول، ثم ينتقل إلى الحلقة for التي ستمر على 10 عناصر. داخل الحلقة، سيطبع البرنامج عددًا صحيحًا عشوائيًا من المجال 1 إلى 25 (مشمول). يُمرّّر العددان الصحيحان 1 و 25 إلى random.randint()‎‎ كمعاملين. عند تنفيذ البرنامج باستخدام الأمرpython my_rand_int.py، ستظهر 10 أعداد صحيحة عشوائية في المخرجات. نظرًا لأنّ هذه العناصر عشوائية، فستحصل على الأرجح على أعداد مختلفة في كل مرة تنفّذ فيها البرنامج، لكنها عمومًا ستبدو كما يلي: 6 9 1 14 3 22 10 1 15 9 الأعداد الصحيحة كلها محصورة بين 1 و 25. إذا كنت ترغب في استخدام دوال من أكثر من وحدة، يمكنك ذلك عن طريق إضافة عدة تعليمات استيراد: import random import math قد تصادف برامج تستورد عدة وحدات مفصولة بفواصل - مثل import random, math - ولكنّ هذا لا يتوافق مع دليل التنسيق PEP 8. للاستفادة من الوحدة الإضافية، يمكننا إضافة الثابت pi من الوحدة math إلى برنامجنا، وتقليل عدد الأعداد الصحيحة العشوائية المطبوعة: import random import math for i in range(5): print(random.randint(1, 25)) print(math.pi) الآن، عند تنفيذ البرنامج، سنحصل على مخرجات على الشكل التالي، مع تقريب للعدد pi في السطر الأخير: 18 10 7 13 10 3.141592653589793 تتيح لك التعليمة import استيراد وحدة واحدة أو أكثر إلى برامجك، وهذ يمكّنك من الاستفادة مما تحويها تلك الوحدات. استخدام الصياغة from ... import للإشارة إلى عناصر من وحدة مستوردة ضمن فضاء الأسماء، يمكنك استخدام التعليمة from ... import. عندما تستورد الوحدات بهذه الطريقة، سيكون بمقدورك الرجوع إلى الدوال بأسمائها فقط، بدلًا من استخدام الصياغة النقطية. في هذه الصياغة، يمكنك تحديد التعريفات التي تود الإشارة إليها مباشرة. في بعض البرامج، قد ترى العبارة * from ... import، إذ تشير العلامة * إلى جميع العناصر الموجودة في الوحدة، ولكنّ هذه الصياغة غير معتمدة في PEP 8. سنحاول في البداية استيراد دالة واحدة من الوحدة random، وهي randint()‎‎: from random import randint هنا، نستدعي أولًا الكلمة المفتاحية from، ثم random. بعد ذلك، نستخدم الكلمة المفتاحية import، ونستدعي الدالة المحددة التي نودّ استخدامها. الآن، عندما نرغب في استخدام هذه الدالة في برنامجنا، لن نستدعي الدالة وفق الصياغة النقطية، random.randint()‎‎، ولكن سنستدعيها باسمها مباشرةً، أي randint()‎‎: from random import randint for i in range(10): print(randint(1, 25)) عند تنفيذ البرنامج، ستتلقى مخرجات مشابهة لما تلقيته مسبقًا. يتيح لنا استخدام from ... import الرجوع إلى العناصر المعرّفة في الوحدة من فضاء الأسماء الخاص ببرنامجنا، مما يتيح لنا تجنب استخدام الصياغة النقطية الطويلة. الأسماء المستعارة في الوحدات يمكن تعديل أسماء الوحدات ودوالها داخل بايثون باستخدام الكلمة المفتاحية as. قد ترغب في تغيير اسم ما لأنك تستخدمه سلفًا في برنامجك، أو أنه مستخدم في وحدة أخرى مستوردة، أو قد ترغب في اختصار اسم طويل تستخدمه كثيرًا. يمكنك ذلك عبر الصياغة التالية: import [module] as [another_name] لنعدّل اسم الوحدة math في ملف البرنامج my_math.py. سنغيّر اسم الوحدة math إلى m من أجل اختصاره. سيبدو برنامجنا المعدل كالتالي: import math as m print(m.pi) print(m.e) سنشير داخل البرنامج إلى الثابت pi بالتعبير m.pi، بدلًا من math.pi. يشيع في بعض الوحدات استخدام أسماء مستعارة (aliases) محدَّدة. فمثلًا، يدعو التوثيق الرسمي للوحدة matplotlib.pyplot إلى استخدام الاسم المستعار plt: import matplotlib.pyplot as plt يسمح هذا للمبرمجين بإلحاق الكلمة القصيرة plt بأي دالة متاحة داخل الوحدة، كما هو الحال في plt.show()‎‎. خلاصة يسمح مفهوم الوحدات باستدعاء دوال غير مضمّنة في بايثون. فبعض الوحدات مُثبّتة كجزء من بايثون، وبعضها سنثبّتها عبر pip. يتيح لنا استخدام الوحدات توسيع برامجنا وتقويتها، لأنها تضع تحت تصرّفنا شفرات جاهزة للاستخدام. يمكننا أيضًا إنشاء وحدات خاصة بنا، لنستخدمها نحن، أو المبرمجون الآخرون وهذا ما سنتعرف عليه في المقال التالي. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة -وبتصرّف- للمقال How To Import Modules in Python 3 لصاحبته Lisa Tagliaferri. اقرأ أيضًا المقالة التالية: كيفية كتابة الوحدات في بايثون 3 المقالة السابقة: فهم القواميس في بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  24. توفر List Comprehensions طريقةً مختصرةً لإنشاء القوائم بناءً على قوائم موجودة مسبقًا. فعند استخدام list comprehensions فيمكن بناء القوائم باستخدام أيّ نوع من البيانات المتسلسلة التي يمكن الدوران على عناصرها عبر حلقات التكرار، بما في ذلك السلاسل النصية و tuples. من ناحية التركيب اللغوي، تحتوي list comprehensions على عنصر يمكن المرور عليه ضمن تعبيرٍ متبوعٍ بحلقة for. ويمكن أن يُتبَع ما سبق بتعابير for أو if إضافية، لذا سيساعدك الفهم العميق لحلقات for والعبارات الشرطية في التعامل مع list comprehensions. توفِّر list comprehensions طريقةً مختلفةً لإنشاء القوائم وغيرها من أنواع البيانات المتسلسلة. وعلى الرغم من إمكانية استخدام الطرائق الأخرى للدوران، مثل حلقات for، لإنشاء القوائم، لكن من المفضَّل استعمال list comprehensions لأنها تقلِّل عدد الأسطر الموجودة في برنامجك. List Comprehensions يمكن بناء list comprehensions في بايثون كالآتي: list_variable = [x for x in iterable] ستُسنَد القائمة، أو أي نوع من البيانات يمكن المرور على عناصره، إلى متغير. المتغيرات الإضافية –التي تُشير إلى عناصر موجودة ضمن نوع البيانات الذي يمكن المرور على عناصره– تُبنى حول عبارة for. والكلمة المحجوزة in تستعمل بنفس استعمالها في حلقات for وذلك لمرور على عناصر iterable. لننظر إلى مثالٍ يُنشِئ قائمةً مبنيةً على سلسلةٍ نصية: shark_letters = [letter for letter in 'shark'] print(shark_letters) أسندنا في المثال السابق قائمةً جديدةً إلى المتغير shark_letters، واستعملنا المتغير letter للإشارة إلى العناصر الموجودة ضمن السلسلة النصية 'shark'. استعملنا بعد ذلك الدالة print()‎ لكي نتأكد من القائمة الناتجة والمُسنَدة إلى المتغير shark_letters، وحصلنا على الناتج الآتي: ['s', 'h', 'a', 'r', 'k'] القائمة التي أنشأناها باستخدام list comprehensions تتألف من العناصر التي تكوِّن السلسلة النصية 'shark'، وهي كل حرف في الكلمة shark. يمكن إعادة كتابة تعابير list comprehensions كحلقات for، لكن لاحظ أنَّك لا تستطيع إعادة كتابة كل حلقة for بصيغة list comprehensions. لنعد كتابة المثال السابق الذي أنشأنا فيه القائمة shark_letters باستخدام حلقة for، وهذا سيساعدنا في فهم كيف تعمل list comprehensions عملها: shark_letters = [] for letter in 'shark': shark_letters.append(letter) print(shark_letters) عند إنشائنا للقائمة عبر استخدام الحلقة for، فيجب تهيئة المتغير الذي سنُسنِد العناصر إليه كقائمة فارغة، وهذا ما فعلناه في أوّل سطر من الشيفرة السابقة. ثم بدأت حلقة for بالدوران على عناصر السلسلة النصية 'shark' مستعملةً المتغير letter للإشارة إلى قيمة العنصر الحالي. ومن ثم أضفنا كل عنصر في السلسلة النصية إلى القائمة ضمن حلقة for وذلك باستخدام الدالة list.append(x). الناتج من حلقة for السابقة يماثل ناتج list comprehension في المثال أعلاه: ['s', 'h', 'a', 'r', 'k'] الخلاصة: يمكن إعادة كتابة List comprehensions كحلقات for، لكن بعض حلقات for يمكن إعادة كتابتها لتصبح List comprehensions لتقليل كمية الشيفرات المكتوبة. استخدام التعابير الشرطية مع List Comprehensions يمكن استخدام التعابير الشرطية في list comprehension لتعديل القوائم أو أنواع البيانات المتسلسلة الأخرى عند إنشاء قوائم جديدة. لننظر إلى مثالٍ عن استخدام العبارة الشرطية if في تعبير list comprehension: fish_tuple = ('blowfish', 'clownfish', 'catfish', 'octopus') fish_list = [fish for fish in fish_tuple if fish != 'octopus'] print(fish_list) استعملنا المتغير fish_tuple الذي من نوع البيانات tuple كأساس للقائمة الجديدة التي سنُنشِئها التي تسمى fish_list. استعملنا for و in كما في القسم السابق، لكننا أضفنا هنا العبارة الشرطية if. ستؤدي العبارة الشرطية if إلى إضافة العناصر غير المساوية للسلسلة النصية 'octopus'، لذا ستحتوي القائمة الجديدة على العناصر الموجودة في بنية tuple والتي لا تُطابِق الكلمة 'octopus'. عند تشغيل البرنامج السابق فسنلاحظ أنَّ القائمة fish_list تحتوي على نفس العناصر التي كانت موجودة في fish_tuple لكن مع حذف العنصر 'octopus': ['blowfish', 'clownfish', 'catfish'] أي أصبحت القائمة الجديدة تحتوي على بنية tuple الأصلية لكن ما عدا السلسلة النصية التي استثنيناها عبر التعبير الشرطي. سنُنشِئ مثالًا آخر يستعمل المعاملات الرياضية والأرقام الصحيحة والدالة range()‎: number_list = [x ** 2 for x in range(10) if x % 2 == 0] print(number_list) القائمة التي ستُنشَأ باسم number_list ستحتوي على مربع جميع القيم الموجودة من المجال 0 إلى 9 لكن إذا كان الرقم قابلًا للقسمة على 2. وستبدو المخرجات كالآتية: [0, 4, 16, 36, 64] دعنا نُفصِّل ما الذي يفعله تعبير list comprehension السابق، ودعنا نفكِّر بالذي سيظهر إذا استعملنا التعبير x for x in range(10) فقط. يجب أن يبدو برنامجنا الصغير كالآتي: number_list = [x for x in range(10)] print(number_list) الناتج: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] لنضف العبارة الشرطية الآن: number_list = [x for x in range(10) if x % 2 == 0] print(number_list) الناتج: [0, 2, 4, 6, 8] أدّت العبارة الشرطية if إلى قبول العناصر القابلة للقسمة على 2 فقط وإضافتها إلى القائمة، مما يؤدي إلى حذف جميع الأرقام الفردية. يمكننا الآن استخدام معامل رياضي لتربيع قيمة المتغير x: number_list = [x ** 2 for x in range(10) if x % 2 == 0] print(number_list) أي ستُربَّع قيم القائمة السابقة ‎[0, 2, 4, 6, 8] وسيُخرَج الناتج الآتي: [0, 4, 16, 36, 64] يمكننا أيضًا استعمال ما يشبه عبارات if المتشعبة في تعابير list comprehension: number_list = [x for x in range(100) if x % 3 == 0 if x % 5 == 0] print(number_list) سيتم التحقق أولًا أنَّ المتغير x قابل للقسمة على الرقم 3، ثم سنتحقق إن كان المتغير x قابل للقسمة على الرقم 5، وإذا حقَّق المتغير x الشرطين السابقين فسيُضاف إلى القائمة، وسيُظهَر في الناتج: [0, 15, 30, 45, 60, 75, 90] الخلاصة: يمكن استخدام عبارات if الشرطية لتحديد ما هي العناصر التي نريد إضافتها إلى القائمة الجديدة. حلقات التكرار المتشعبة في تعابير List Comprehension يمكن استعمال حلقات التكرار المتشعبة لإجراء عدِّة عمليات دوران متداخلة في برامجنا. سننظر في هذا القسم إلى حلقة for متشعبة وسنحاول تحويلها إلى تعبير list comprehension. هذه الشيفرة ستُنشِئ قائمةً جديدةً بالدوران على قائمتين وبإجراء عمليات رياضية عليها: my_list = [] for x in [20, 40, 60]: for y in [2, 4, 6]: my_list.append(x * y) print(my_list) سنحصل على الناتج الآتي عند تشغيل البرنامج: [40, 80, 120, 80, 160, 240, 120, 240, 360] الشيفرة السابقة تضرب العناصر الموجودة في أوّل قائمة بالعناصر الموجودة في ثاني قائمة في كل دورة. لتحويل ما سبق إلى تعبير list comprehension، وذلك باختصار السطرين الموجودين في الشيفرة السابقة وتحويلهما إلى سطرٍ وحيدٍ، الذي يبدأ بإجراء العملية x*y، ثم ستلي هذه العملية حلقة for الخارجية، ثم يليها حلقة for الداخلية؛ وسنضيف تعبير print()‎ للتأكد أنَّ ناتج القائمة الجديدة يُطابِق ناتج البرنامج الذي فيه حلقتين متداخلتين: my_list = [x * y for x in [20, 40, 60] for y in [2, 4, 6]] print(my_list) الناتج: [40, 80, 120, 80, 160, 240, 120, 240, 360] أدى استعمال تعبير list comprehension في المثال السابق إلى تبسيط حلقتَي for لتصبحا سطرًا وحيدًا، لكن مع إنشاء نفس القائمة والتي ستُسنَد إلى المتغير my_list. توفِّر لنا تعابير list comprehension طريقةً بسيطةً لإنشاء القوائم، مما يسمح لنا باختصار عدِّة أسطر إلى سطرٍ وحيد. لكن من المهم أن تبقي في ذهنك أنَّ سهولة قراءة الشيفرة لها الأولوية دومًا، لذا إذا أصبحتَ تعابير list comprehension طويلةً جدًا ومعقدة، فمن الأفضل حينها تحويلها إلى حلقات تكرار عادية. الخلاصة تسمح تعابير list comprehension لنا بتحويل قائمة أو أي نوع من البيانات المتسلسلة إلى سلسلةٍ جديدة، ولها شكلٌ بسيطٌ يُقلِّل عدد الأسطر التي نكتبها. تتبع تعابير list comprehension شكلًا رياضيًا معيّنًا، لذا قد يجدها المبرمجون أولو الخلفية الرياضية سهلة الفهم. وصحيحٌ أنَّ تعابير list comprehension تختصر الشيفرةـ لكن من المهم جعل سهولة قراءة الشيفرة من أولوياتنا، وحاول تجنّب الأسطر الطويلة لتسهيل قراءة الشيفرة. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة –وبتصرّف– للمقال Understanding List Comprehensions in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة التالية: فهم نوع البيانات Tuples في بايثون 3 المقالة السابقة: كيفية استخدام توابع القوائم في بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  25. يتضمّن بايثون عددًا من هياكل البيانات، بما في ذلك القوائم. تمكّن هياكل البيانات من تنظيم البيانات وتخزينها عبر توابع خاصة مُضمّنة في بايثون. للاستفادة من محتويات هذه المقالة، ينبغي أن تكون على إلمام بأساسيات القوائم، وصياغتها وكيفية فهرستها. يمكنك تعلم ذلك من المقالة السابقة: القوائم في بايثون 3. سنتعرف على التوابع المُضمنة التي يمكن استخدامها مع القوائم. ونتعلم كيفية إضافة عناصر إلى القائمة وكيفية إزالتها، وتوسيع القوائم وترتيبها، وغير ذلك. القوائم أنواعٌ قابلةٌ للتغيير (mutable) على عكس السلاسل النصية التي لا يمكن تغييرها، فعندما تستخدم تابعًا على قائمة ما، ستؤثر في القائمة نفسها، وليس في نسخة منها. سنعمل في هذه المقالة على قائمة تمثل حوض سمك، إذ ستحوي القائمة أسماء أنواع الأسماك الموجودة في الحوض، وسنعدلها كلما أضفنا أسماكًا أو أزلناها من الحوض. التابع list.append()‎‎ يضيف التابع list.append(x)‎ عنصرًا (x) إلى نهاية القائمة. يُعرّف المثال التالي قائمةً تمثل الأسماك الموجودة في حوض السمك. fish = ['barracuda','cod','devil ray','eel'] تتألف هذه القائمة من 4 سلاسل نصية، وتتراوح فهارسها من 0 إلى 3. سنضيف سمكة جديدة إلى الحوض، ونود بالمقابل أن نضيف تلك السمكة إلى قائمتنا. سنمرّر السلسلة النصية flounder التي تمثل نوع السمكة الجديدة إلى التابع list.append()‎، ثم نطبع قائمتنا المعدلة لتأكيد إضافة العنصر. fish.append('flounder') print(fish) # ['barracuda', 'cod', 'devil ray', 'eel', 'flounder'] الآن، صارت لدينا قائمة من 5 عناصر، تنتهي بالعنصر الذي أضفناه للتو عبر التابع append()‎. التابع list.insert()‎ يأخذ التابع list.insert (i,x)‎ وسيطين: الأول i يمثِّل الفهرس الذي ترغب في إضافة العنصر عنده، و x يمثل العنصر نفسه. لقد أضفنا إلى حوض السمك سمكة جديدة من نوع anchovy. ربما لاحظت أنّ قائمة الأسماك مرتبة ترتيبًا أبجديًا حتى الآن. لهذا السبب لا نريد إفساد الترتيب، لذا لن نضيف السلسلة النصية anchovy إلى نهاية القائمة باستخدام الدالة list.append()‎. بدلًا من ذلك، سنستخدم التابع list.insert()‎ لإضافة anchovy إلى بداية القائمة، أي عند الفهرس 0: fish.insert(0,'anchovy') print(fish) # ['anchovy', 'barracuda', 'cod', 'devil ray', 'eel', 'flounder'] في هذه الحالة، أضفنا العنصر إلى بداية القائمة. ستتقدم فهارس العناصر التالية خطوةً واحدةً إلى الأمام. لذلك، سيصبح العنصر barracuda عند الفهرس 1، والعنصر cod عند الفهرس 2، والعنصر flounder - الأخير - عند الفهرس 5. سنحضر الآن سمكة من نوع damselfish إلى الحوض، ونرغب في الحفاظ على الترتيب الأبجدي لعناصر القائمة أعلاه، لذلك سنضع هذا العنصر عند الفهرس 3: fish.insert(3,'damselfish')‎. التابع list.extend()‎ إذا أردت أن توسّع قائمة بعناصر قائمة أخرى، فيمكنك استخدام التابع list.extend(L)‎، والذي يأخذ قائمة كمعامل. سنضع في الحوض أربعة أسماك جديدة. أنواع هذه الأسماك مجموعة معًا في القائمة more_fish: more_fish = ['goby','herring','ide','kissing gourami'] سنضيف الآن عناصر القائمة more_fish إلى قائمة الأسماك، ونطبع القائمة لنتأكد من أنّ عناصر القائمة الثانية قد ضُمّت إليها: fish.extend(more_fish) print(fish) ستطبع بايثون القائمة التالية: ['anchovy', 'barracuda', 'cod', 'devil ray', 'eel', 'flounder', 'goby', 'herring', 'ide', 'kissing gourami'] في هذه المرحلة، صارت القائمة fish تتألف من 10 عناصر. التابع list.remove()‎ لإزالة عنصر من قائمة، استخدم التابع list.remove(x)‎، والذي يزيل أول عنصر من القائمة له القيمة المُمرّرة x. جاءت مجموعة من العلماء المحليين لزيارة الحوض. سيجرون أبحاثًا عن النوع kissing gourami، وطلبوا استعارة السمكة kissing gourami، لذلك نود إزالة العنصر kissing gourami من القائمة لنعكس هذا التغيير: fish.remove('kissing gourami') print(fish) والمخرجات ستكون: ['anchovy', 'barracuda', 'cod', 'devil ray', 'eel', 'flounder', 'goby', 'herring', 'ide'] بعد استخدام التابع list.remove()‎، لم يعد العنصر kissing gourami موجودًا في القائمة. في حال مرّرت عنصرًا x غير موجود في القائمة إلى التابع list.remove()‎، فسيُطلق الخطأ التالي: ValueError: list.remove(x): x not in list التابع list.remove()‎ لن يزيل إلا أول عنصر تساوي قيمته قيمة العنصر المُمرّر إلى التابع، لذلك إن كانت لدينا سمكتان من النوع kissing gourami في الحوض، وأعرنا إحداهما فقط للعلماء، فإنّ التعبير fish.remove('kissing gourami')‎ لن يمحو إلا العنصر الأول المطابق فقط. التابع list.pop()‎ يعيد التابع list.pop ()‎ العنصر الموجود عند الفهرس المحدد من القائمة، ثم يزيل ذلك العنصر. تشير الأقواس المربعة حول i إلى أنّ هذا المعامل اختياري، لذا، إذا لم تحدد فهرسًا (كما في fish.pop()‎)، فسيُعاد العنصر الأخير ثم يُزال. لقد أصبح حجم السمكة devil ray كبيرًا جدًا، ولم يعد الحوض يسعها، ولحسن الحظ أنّ هناك حوض سمك في بلدة مجاورة يمكنه استيعابها. سنستخدم التابع ‎.‎pop()‎، ونمرر إليه العدد 3، الذي يساوي فهرس العنصر devil ray، بقصد إزالته من القائمة. بعد إعادة العنصر، سنتأكد من أننا أزلنا العنصر الصحيح. print(fish.pop(3)) # devil ray print(fish) # ['anchovy', 'barracuda', 'cod', 'eel', 'flounder', 'goby', 'herring', 'ide'] باستخدام التابع ‎.pop()‎ تمكّنا من إعادة وإزالة ray devil من قائمة الأسماك. إذا لم نمرر أيّ معامل إلى هذا التابع، ونفّذنا التعبير fish.pop()‎، فسيُعاد العنصر الأخير ide ثم يُزَال من القائمة. التابع list.index()‎ يصعب في القوائم الكبيرة تحديد فهارس العناصر التي تحمل قيمة معينة. لأجل ذلك، يمكننا استخدام التابع list.index(x)‎، حيث يمثل الوسيطx قيمة العنصر المبحوث عنه، والذي نريد إعادة فهرسه. إذا كان هناك أكثر من عنصر واحد يحمل القيمة x، فسيُعَاد فهرس العنصر الأول. print(fish) # ['anchovy', 'barracuda', 'cod', 'eel', 'flounder', 'goby', 'herring', 'ide'] print(fish.index('herring')) # 6 سوف يُطلق خطأ في حال مرّرنا قيمة غير موجودة في القائمة إلى التابع ‎.index()‎. التابع list.copy()‎ أحيانُا نرغب في تعديل عناصر قائمةٍ والتجريب عليها، مع الحفاظ على القائمة الأصلية دون تغيير؛ يمكننا في هذه الحالة استخدام التابع list.copy()‎ لإنشاء نسخة من القائمة الأصلية. في المثال التالي، سنمرّر القيمة المعادة من fish.copy()‎ إلى المتغير fish_2، ثم نطبع قيمة fish_2 للتأكد من أنها تحتوي على نفس عناصر القائمة fish. fish_2 = fish.copy() print(fish_2) # ['anchovy', 'barracuda', 'cod', 'eel', 'flounder', 'goby', 'herring', 'ide'] في هذه المرحلة، القائمتان fish و fish_2 متساويتان. التابع list.reverse()‎ يمكننا عكس ترتيب عناصر قائمة باستخدام التابع list.reverse()‎. في المثال التالي سنستخدم التابع ‎.reverse()‎ مع القائمة fish لعكس ترتيب عناصرها. fish.reverse() print(fish) # ['ide', 'herring', 'goby', 'flounder', 'eel', 'cod', 'barracuda', 'anchovy'] بعد استخدام التابع ‎.reverse()‎، صارت القائمة تبدأ بالعنصر ide، والذي كان في نهاية القائمة من قبل، كما ستنتهي القائمة بالعنصر anchovy، والذي كان في بداية القائمة من قبل. التابع list.count()‎ يعيد التابع list.count(x)‎ عدد مرات ظهور القيمة x في القائمة. هذا التابع مفيد في حال كنا نعمل على قائمة طويلة بها الكثير من القيم المتطابقة. إذا كان حوض السمك كبيرًا، على سبيل المثال، وكانت عندنا عدة أسماك من النوع neon tetra، فيمكننا استخدام التابع ‎.count()‎ لتحديد العدد الإجمالي لأسماك هذا النوع. في هذا المثال سنحسب عدد مرات ظهور العنصر goby: print(fish.count('goby')) # 1 تظهر السلسلة النصية goby مرةً واحدةً فقط في القائمة، لذا سيُعيد التابع ‎.count()‎ العدد 1. يمكننا استخدام هذا التابع أيضًا مع قائمة مكوَّنة من أعداد صحيحة. المثال التالي يوضح ذلك. يتتبع المشرفون على الحوض أعمار الأسماك الموجودة فيه للتأكد من أنّ وجباتها الغذائية مناسبة لأعمارها. هذه القائمة الثانية المُسماة fish_ages تتوافق مع أنواع السمك في القائمة fish. نظرًا لأنّ الأسماك التي لا يتجاوز عمرها عامًا واحدًا لها احتياجات غذائية خاصة، فسنحسب عدد الأسماك التي عمرها عامًا واحدًا: fish_ages = [1,2,4,3,2,1,1,2] print(fish_ages.count(1)) # 3 يظهر العدد الصحيح 1 في القائمة fish_ages ثلاث مرات، لذلك يعيد التابع ‎.count()‎ العدد 3. التابع list.sort()‎ يُستخدم التابع list.sort()‎ لترتيب عناصر القائمة التي استُدعِي معها . سنستخدم قائمة الأعداد الصحيحة fish_ages لتجريب التابع ‎.sort()‎: fish_ages.sort() print(fish_ages) # [1, 1, 1, 2, 2, 2, 3, 4] باستدعاء التابع ‎.sort()‎ مع القائمة fish_ages، ستُعاد قائمة الأعداد الصحيحة مرتبةً. التابع list.clear()‎ بعد الانتهاء من العمل على قائمة ما، يمكنك إزالة جميع القيم الموجودة فيها باستخدام التابع list.clear()‎. قررت الحكومة المحلية الاستيلاء على حوض السمك الخاص بنا، وجعله مساحة عامة يستمتع بها سكان مدينتنا. نظرًا لأننا لم نعد نعمل على الحوض، فلم نعد بحاجة إلى الاحتفاظ بقائمة الأسماك، لذلك سنزيل عناصر القائمة fish: fish.clear() print(fish) # [] نرى في المخرجات أقواسًا معقوفة نتيجة استدعاء التابع ‎.clear()‎ على القائمة fish، وهذا تأكيد على أنّ القائمة أصبحت خالية من جميع العناصر. خلاصة لمَّا كانت القوائم تسلسلات قابلة للتغيير (mutable)، فإنّها هياكلُ بيانات مرنة ومفيدة للغاية. كما تتيح لنا توابع القوائم إجراء العديد من العمليات على القوائم بسهولة، إذ يمكننا استخدام التوابع لتعديل القوائم وترتيبها ومعالجتها بفعالية. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة -وبتصرّف- للمقال How To Use List Methods in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة التالية: فهم كيفية استعمال List Comprehensions في بايثون 3 المقالة السابقة: مدخل إلى القوائم في بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
×
×
  • أضف...