إنشاء الدوال واستخدامها في البرمجة

إنشاء الدوال الخاصة بك

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

ولكن قبل أن تتعرف على إنشاء الدوال، يمكنك التعرف على الفئات الأربع للمهام في برنامج الكمبيوتر.

مهام البرمجة الأساسية

يمكنك التفكير في كل ما يفعله برنامج الكمبيوتر ليندرج في واحدة من أربع فئات:

  • التخزين
  • التكرار
  • اتخاذ القرار
  • إعادة الاستخدام

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

التخزين Store

تعتمد برامج الكمبيوتر على البيانات. يمكن أن تكون البيانات أي شيء من رقم أو اسم واحد إلى مجموعة بيانات كبيرة ومعقدة. في لعبة Angry Goblin، تضمنت البيانات التي تحتاجها ما يلي:

  • اسم اللاعب
  • عدد الأبواب في اللعبة
  • مكان العفريت
  • تخمين اللاعب
  • العلم للإشارة إلى ما إذا كانت اللعبة لا تزال قيد التشغيل، والذي تستخدمه في جملة while

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

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

التكرار Repeat

أحد المظاهر الأساسية لمبدأ الكود الجاف DRY هو عندما تريد تكرار بعض الإجراءات عدة مرات متتالية. أنت لا تريد تكرار الأشياء بنفسك، لذا عليك أن تجعل الكمبيوتر يكررها بدلاً من ذلك. سيكون هناك العديد من الأشياء التي تتطلب تكرار جزء من التعليمات البرمجية في برامج الكمبيوتر التي ستكتبها. لقد تعرفت على الحلقات for و  whileوهما طريقتان لإنشاء الحلقات في بايثون Python.

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

اتخاذ القرار Decide

الإجراء الشائع الآخر الذي يقوم به برنامج الكمبيوتر هو تحديد مسار العمل الذي يحتاج إلى اتخاذه. هذه هي القرارات التي يمكن للبرنامج فقط اتخاذها لأنها تعتمد على أشياء أخرى تحدث أثناء تشغيل البرنامج. العبارة الشرطية if هي جملة بايثون Python التي تتعامل مع برامج الكمبيوتر الذي يتخذ القرارات.

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

إعادة الاستخدام Reuse

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

طريقة إعادة استخدام الكود أينما احتجت إليه هي استخدام الدوال أو الوظائف Functions. لقد استخدمت العديد من الدوال بالفعل. كلما احتجت إلى عرض بعض المعلومات للمستخدم، يمكنك استدعاء دالة print ()، وكلما أردت الحصول على رقم عشوائي، يمكنك استدعاء دالة randint () من الوحدة النمطية random .

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

إنشاء الدوال للبحث عن الأسماء

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

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

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

أولاً، حل المشكلة، ثم اكتب الكود

الاقتباس المعروف في البرمجة هو التالي:

أولا، حل المشكلة. ثم اكتب الكود.

جون جونسون

قد تبدو هذه الجملة واضحةً، لكنها تسلط الضوء على نقطتين أساسيتين عند تطوير البرنامج:

  • التمييز بين المعرفة الخاصة بالموضوع والمعرفة الخاصة بالبرمجة
  • أهمية التخطيط لمهمة البرمجة الخاصة بك قبل البدء في كتابة التعليمات البرمجية

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

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

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

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

في كثير من الأحيان، لحل مشكلتك الخاصة بموضوع معين، قد ترغب في أن تسأل نفسك السؤال التالي: كيف يمكنني تنفيذ هذه المهمة يدويًا، باستخدام القلم والورقة، بافتراض أن الوقت لم يكن مشكلة؟

حل مشكلة البحث عن الأسماء

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

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

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

مستعد؟

يمكنك الآن القراءة. فيما يلي مجموعة من الخطوات التي يمكن أن تصف هذه المهمة:

  1. انظر إلى الاسم الأول في القائمة
  2. انظر إلى الحرف الأول من هذا الاسم
  3. إذا كان الحرف هو الذي تريده، فاكتب الاسم في قائمة جديدة
  4. إذا لم يكن الحرف هو المطلوب، فلا تفعل شيئًا
  5. انظر إلى الاسم الثاني في القائمة ثم كرر الخطوات من 2 إلى 4
  6. كرر الخطوات من 2 إلى 4 لجميع الأسماء الموجودة في القائمة

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

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

ترجمة الأفكار من الإنجليزية إلى لغة بايثون

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

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

list_of_names = ['Amelia', 'Olivia', 'Emily', 'Alexey', 'Poppy', 'Ava', 'Isabella', 'Jessica', 'Marcus', 'Lily', 'Sophie', 'Grace', 'Vsevolod', 'Sophia', 'Mia', 'Evie', 'Ruby', 'Celim', 'Sumir', 'Ella', 'Scarlett', 'Ruben', 'Isabelle', 'Chloe', 'Cherlin', 'Sienna', 'Masha', 'Freya', 'Phoebe', 'Charlotte', 'Daisy', 'Alice', 'Florence', 'Eva', 'Sofia', 'Millie', 'Lucy', 'Evelyn', 'Elsie', 'Rosie', 'Imogen', 'Lola', 'Matilda', 'Elizabeth', 'Layla', 'Alasdair','Holly', 'Lilly', 'Molly', 'Erin', 'Ellie', 'Maisie', 'Maya', 'Abigail', 'Eliza', 'Georgia', 'Jasmine', 'Esme', 'Willow', 'Leanne', 'Bella', 'Annabelle', 'Keemiya', 'Ivy', 'Amber', 'Emilia', 'Emma', 'Summer', 'Hannah', 'Eleanor', 'Harriet', 'Rose', 'Amelie', 'Lexi', 'Megan', 'Gracie', 'Zara', 'Nuha', 'John', 'Lacey', 'Martha', 'Anna', 'Violet', 'Darcey', 'Maria', 'Maryam', 'Brooke', 'Aisha', 'Katie', 'Leah', 'Heinrich', 'Nour', 'Thea', 'Darcie', 'Hollie', 'Amy', 'Alexandra', 'Stephen', 'Jonathan', 'Penny', 'Mollie', 'Heidi', 'Lottie', 'Bethany', 'Francesca', 'Faith', 'Harper', 'Nancy', 'Beatrice', 'Isabel', 'Juliette', 'Darcy', 'Lydia', 'Sarah', 'Sara', 'Julia', 'Victoria', 'Zoe', 'Robyn', 'Oliver', 'Jack', 'Harry', 'Jacob', 'Charlie', 'Thomas', 'Annabel', 'George', 'Oscar', 'James', 'Ian', 'William', 'Noah', 'Alfie', 'Joshua', 'Yuvraj', 'Muhammad', 'Leo', 'Archie', 'Ethan', 'Joseph', 'Arushi', 'Freddie', 'Samuel', 'Alexander', 'Logan', 'Daniel', 'Isaac', 'Max', 'Mohammed', 'Benjamin', 'Hugo', 'Mason', 'Lucas', 'Edward', 'Harrison', 'Jake', 'Neil', 'Dylan', 'Asher', 'Riley', 'Akash', 'Finley', 'Catherine', 'Theo', 'Muktarsi', 'Sebastian', 'Adam', 'Zachary', 'Arthur', 'Thomas', 'Alberto', 'Toby', 'Jayden', 'Luke', 'Harley', 'Lewis', 'Tyler', 'Harvey', 'Anusha', 'Matthew', 'David', 'Reuben', 'Alok', 'Michael', 'Elijah', 'Kian', 'Tom', 'Mohammad', 'Blake', 'Jean', 'Luca', 'Theodore', 'Stanley', 'Derin', 'Jenson', 'Nathan', 'Nicholas', 'Charles', 'Frankie', 'Constantin', 'Jude', 'Teddy', 'Eric', 'Viren', 'Louie', 'Louis', 'Ryan', 'Hugo', 'Bobby', 'Niamh', 'Anya', 'Elliott', 'Dexter', 'Khai', 'Hariesh', 'Henry', 'Ollie', 'Aron', 'Alex', 'Liam', 'Kai', 'Gabriel', 'Connor', 'Aaron', 'Afrah', 'Frederick', 'Callum', 'Lorcan', 'Elliot', 'Albert', 'Leon', 'Ronnie', 'Rory', 'Jamie', 'Austin', 'Seth', 'Ibrahim', 'Mei', 'Owen', 'Caleb', 'Yousuf', 'Ellis', 'Sonny', 'Devyn', 'Robert', 'Joey', 'Felix', 'Finlay', 'Rossa', 'Ekraj', 'Jackson', 'Jimi', 'Meera', 'Rafi', 'Salahdeen', 'Guido', 'Tanya', 'Karlis']

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

تنزيل مستودع ملفات كتاب البرمجة بلغة بايثون Python

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

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

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

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

نصيحة: في PyCharm، إذا كان الشريط الجانبي للمشروع مفتوحًا، يمكنك النقر فوق اسم المشروع (السطر العلوي) ثم إظهار القائمة السياقية بنقرة تحكم (Mac) أو النقر بزر الماوس الأيمن (Windows / Linux). سيكون أحد الخيارات هو إظهار في Finder أو إظهار في Explorer اعتمادًا على نظام التشغيل الذي تستخدمه.

إنشاء الدالة التي تقوم بالمهمة

ستحتاج إلى ملف names.py من مستودع الملفات لهذا المشروع. ستحتاج إلى وضع هذا الملف في مجلد مشروعك.

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

# list_of_names = [...]

name = list_of_names[0]
print(name)

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

التخزين Store

يمثل الفهرس “صفر” العنصر الأول في القائمة، والذي سيخزنه البرنامج في المتغير name. يعرض الإخراج من الكود أعلاه الاسم الأول في القائمة:

Amelia

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

print(name[0])

أنت تستخرج العنصر الأول من اسم السلسلة:

A

اتخاذ القرار

حان وقت القرار الآن. يجب أن يقرر برنامجك ما يجب فعله بهذا الاسم. لذلك ستحتاج إلى استخدام عبارة if:

# list_of_names = [...]

name = list_of_names[0]
if name[0] == "P":
    print(name)

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

التكرار Repeat

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

من الممكن استخدام حلقة for مع دالة النطاق range () ثم تغيير الرقم الذي تستخدمه عند فهرسة قائمة الأسماء list_of_names للحصول على اسم مختلف من القائمة في كل مرة. ومع ذلك، هناك طريقة أكثر بايثونية لكتابة هذه الحلقة. يمكنك التكرار مباشرة من خلال القائمة:

# list_of_names = [...]

for name in list_of_names:
    # name = list_of_names[0]
    if name[0] == "P":
        print(name)

تم تعريف المتغير name الآن في عبارة for. لذلك سيتم تخصيص كل عنصر في القائمة له، واحدًا تلو الآخر. لم تعد مهمة التعيين  name = list_of_names[0] مطلوبة الآن. يتم الآن تنفيذ هذا الإجراء مباشرةً كجزء من حلقة for.

تم التعليق على هذه المهمة في الكود أعلاه عن طريق إضافة رمز التجزئة # قبلها. يتجاهل برنامج الكمبيوتر أي كود يتبع رمز التجزئة # ولن يتم تنفيذه.

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

Poppy
Phoebe
Penny

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

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

التخزين الصحيح

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

# list_of_names = [...]

result = []
for name in list_of_names:
    if name[0] == "P":
        result.append(name)

print(result)

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

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

result.append(name)

ستدرك أن append() هي دالة لأنها مكتوبة بأحرف صغيرة ويتبعها أقواس. تتم إضافة محتويات المتغير name إلى نهاية القائمة result. نمط هذا الخط يشبه إلى حد ما تعبير آخر استخدمته من قبل، random.randint (). ومع ذلك، هناك فرق كبير.

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

ستقرأ المزيد عن هذه البنية في الفصل التالي عندما تستكشف أنواع البيانات بشكل أكبر. ستتعرف على الإجراءات مثل append() التي يمكنك تنفيذها على أنواع بيانات مختلفة.

إنشاء الدوال (تعريفها)

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

يمكنك إنشاء دالة خاصة بك تسمى find_names_starting_with (). يمكنك إنشاء دالة جديدة باستخدام الكلمة الأساسية def، والتي تعني اختصار كلمة تعريف Definition:

# list_of_names = [...]

def find_names_starting_with():
    result = []
    for name in list_of_names:
        if name[0] == "P":
            result.append(name)

في السطر الأول، أنت “تعلّم” برنامج الكمبيوتر الخاص بك كلمة جديدة، وبشكل أكثر تحديدًا، دالة جديدة. يمكنك اختيار أي اسم تريده للدالة الجديدة. يجب عليك اتباع أفضل الممارسات الحديثة واختيار اسم يصف بوضوح وظيفة الدالة.

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

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

يعلّم إنشاء وتعريف الدالة برنامج الكمبيوتر بالكلمة الجديدة التي أنشأتها للتو. أنت تُعلم برنامجك أنه عندما تكتب find_names_starting_with () لاحقًا في البرنامج، فهذه هي سطور التعليمات البرمجية التي تريد أن ينفذها.

تنفيذ الدالة التي يتم إنشاؤها

إذا كنت تريد تنفيذ الدالة، فستحتاج إلى استدعاء الدالة call the function. استدعاء دالة هو عندما تكتب اسم الدالة متبوعًا بأقواس. تطلب الأقواس من البرنامج تنفيذ الدالة:

# list_of_names = [...]

def find_names_starting_with():
    result = []
    for name in list_of_names:
        if name[0] == "P":
            result.append(name)

find_names_starting_with()

عند تشغيل الكود أعلاه، سيعمل البرنامج بالترتيب التالي:

  • def find_names_starting_with ():

يتم تنفيذ هذا السطر أولاً. يخبر البرنامج أنك تريد تحديد دالة تسمى find_names_starting_with (). لم يتم تنفيذ تعريف الدالة في هذا الوقت.

  • find_names_starting_with ()

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

  • كتلة مسافة بادئة داخل تعريف الدالة

يعود البرنامج الآن إلى حيث تم تعريف الدالة، ويتم تنفيذ الكود في تعريف الدالة.

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

# list_of_names = [...]

def find_names_starting_with():
    result = []
    for name in list_of_names:
        if name[0] == "P":
            result.append(name)

find_names_starting_with()
print(result)

سيؤدي تشغيل هذا الرمز إلى ظهور خطأ:

Traceback (most recent call last):
  File "<path>/<filename>.py", line 10, in <module>
    print(result)
NameError: name 'result' is not defined

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

المتغيرات المحلية والنطاق

يشار أحيانًا إلى الدوال على أنها إجراءات فرعية sub-routines. أنا أفضل مصطلح البرنامج المصغر mini-program. الدالة عبارة عن برنامج صغير قائم بذاته يمكن للبرنامج الرئيسي استدعائها وتنفيذها. يمكن أيضًا أن تقوم دالة أخرى أو برنامج آخر باستدعائها.

نظرًا لأن الدالة هي وحدة شفرة قائمة بذاتها، فإن أي متغير يتم إنشاؤه داخل الدالة يظل داخل الدالة. هذه المتغيرات غير متوفرة خارجها. هذه الحقيقة هي سبب ظهور رسالة الخطأ التي تفيد بأن الاسم “result” غير معروف. المتغير result غير موجود في منطقة البرنامج الرئيسية لأنه موجود فقط داخل الدالة.

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

تشتت؟ سنناقش هذا الأمر لاحقًا في هذا الفصل.

إرجاع البيانات

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

# list_of_names = [...]

def find_names_starting_with():
    result = []
    for name in list_of_names:
        if name[0] == "P":
            result.append(name)
            
    return result

find_names_starting_with()
print(result)

لاحظ مستوى المسافة البادئة لجملة الإرجاع return. إنها جزء من الكتلة def ولكنها ليس جزءًا من كتل for أو if. لذلك فهي تحتاج فقط مسافة بادئة واحدة.

لم تصل إلى هناك بعد – لا يزال هناك خطأ عند تشغيل هذا الرمز. دعونا نرى لماذا.

تصحيح الخطأ

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

هذا التمييز هو سبب حصولك على الخطأ التالي عند تشغيل أحدث إصدار من التعليمات البرمجية:

Traceback (most recent call last):
  File "<path>/<filename>.py", line 12, in <module>
    print(result)
NameError: name 'result' is not defined

لم ترجع الدالة find_names_starting_with () الصندوق المسمى result إلى البرنامج الرئيسي. لذلك لا يمكن للبرنامج الرئيسي العثور على أي إشارة لـ result. أعادت الدالة محتويات الصندوق، وهو عبارة عن قائمة تحتوي على سلاسل. يجب تخزين هذه القائمة في صندوق جديد إذا كنت تريد الاحتفاظ بهذه المعلومات. خلاف ذلك، يتم فقد المعلومات. تحتاج إلى تعيين البيانات التي يتم إرجاعها من الدالة إلى متغير:

# list_of_names = [...]

def find_names_starting_with():
    result = []
    for name in list_of_names:
        if name[0] == "P":
            result.append(name)

    return result

result = find_names_starting_with()
print(result)

لقد أنشأت الآن صندوقًا يسمى result في البرنامج الرئيسي، وقد استخدمته لجمع البيانات التي تم إرجاعها من find_names_starting_with (). هذا المفهوم هو نفسه عندما جمعت البيانات التي تم إرجاعها من unput () في متغير عندما استخدمت دالة input() في لعبة Angry Goblin.

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

# list_of_names = [...]

def find_names_starting_with():
    result = []
    for name in list_of_names:
        if name[0] == "P":
            result.append(name)

    return result

names_p = find_names_starting_with()
print(names_p)

ستحصل الآن على ناتج من هذا الرمز:

['Poppy', 'Phoebe', 'Penny']

المعلمات والوسيطات

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

يمكنك جعل هذه الدالة أكثر مرونة. في الوقت الحالي، يمكن للدالة تصفية الأسماء التي تبدأ بالحرف P. ولن يكون مفيدًا إذا كنا نبحث عن أسماء تبدأ بالحرف A.

يسمى السطر الأول في تعريف الدالة توقيع الدالة signature. يمكنك إعادة كتابة توقيع الدالة على النحو التالي:

def find_names_starting_with(letter):

ما زلت تطلب من البرنامج تعلم اسم دالة جديد بهذا السطر، لكنك الآن تسمح للبرنامج بمعرفة أنه ستكون هناك بعض المعلومات بين الأقواس عند استدعاء الدالة. يتم تخزين هذه المعلومات في صندوق يسمى letter  داخل الدالة. والاسم letter يُسمى “معلمة” parameter.

ستحتاج أيضًا إلى تعديل استدعاء الدالة:

names_p = find_names_starting_with("P")

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

  • المعلمة parameter هي letter
  • الوسطية argument هي “P”

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

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

هناك تغيير آخر تحتاج إلى إجرائه على تعريف الدالة عند إنشائها:

# list_of_names = [...]

def find_names_starting_with(letter):
    result = []
    for name in list_of_names:
        if name[0] == letter:
            result.append(name)

    return result

names_p = find_names_starting_with("P")
print(names_p)

لم تعد بحاجة إلى التحقق مما إذا كان   name[0] يساوي السلسلة “P”. بدلاً من ذلك، يمكنك استخدام تسمية الصندوق. هذه التسمية هي اسم المعلمة. يمكنك الآن استدعاء الدالة كما تشاء، باستخدام وسيطات إدخال مختلفة:

# list_of_names = [...]

def find_names_starting_with(letter):
    result = []
    for name in list_of_names:
        if name[0] == letter:
            result.append(name)

    return result

names_p = find_names_starting_with("P")
print(names_p)

names_a = find_names_starting_with("A")
print(names_a)

names_b = find_names_starting_with("B")
print(names_b)

لقد قمت باستدعاء الدالة ثلاث مرات باستخدام “P” و “A” و “B” كوسيطات إدخال، وقمت بتعيين القوائم التي يعرضها استدعاء كل دالة إلى متغيرات مختلفة. يُظهر الإخراج جميع القوائم الثلاث:

['Poppy', 'Phoebe', 'Penny']
['Amelia', 'Alexey', 'Ava', 'Alice', 'Alasdair', 'Abigail', 'Annabelle', 'Amber', 'Amelie', 'Anna', 'Aisha', 'Amy', 'Alexandra', 'Annabel', 'Alfie', 'Archie', 'Arushi', 'Alexander', 'Asher', 'Akash', 'Adam', 'Arthur', 'Alberto', 'Anusha', 'Alok', 'Anya', 'Aron', 'Alex', 'Aaron', 'Afrah', 'Albert', 'Austin']['Bella', 'Brooke', 'Bethany', 'Beatrice', 'Benjamin', 'Blake', 'Bobby']

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

>>> my_numbers = [4, 6, 12, 9, 6, 23]
>>> len(my_numbers)
6

>>> name = "Stephen"
>>> len(name)
7

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

قبل الاستمرار في القراءة، حاول كتابة الدالة find_names_of_length ().

هل انتهيت من محاولتك؟ يمكنك القراءة.

إليك الكود الذي يوضح كلتا الدالتين اللتين كتبتهما حتى الآن:

# list_of_names = [...]

def find_names_starting_with(letter):
    result = []
    for name in list_of_names:
        if name[0] == letter:
            result.append(name)

    return result

def find_names_of_length(length):
    result = []
    for name in list_of_names:
        if len(name) == length:
            result.append(name)

    return result

names_p = find_names_starting_with("P")
print(names_p)

names_9 = find_names_of_length(9)
print(names_9)

يعرض هذا الرمز الإخراج التالي:

['Poppy', 'Phoebe', 'Penny']['Charlotte', 'Elizabeth', 'Annabelle', 'Alexandra', 'Francesca', 'Alexander', 'Catherine', 'Sebastian', 'Frederick', 'Salahdeen']

يظهر الإخراج قائمتين. تحتوي القائمة الأولى على جميع الأسماء التي تبدأ بـحرف P، وتحتوي القائمة الثانية على جميع الأسماء المكوّنة من 9 أحرف. ماذا لو كنت تريد العثور على جميع الأسماء التي تبدأ بحرف E وتتكون من ستة أحرف؟ يمكنك كتابة دالة أخرى تجمع بين كلتا الميزتين، والتي قد تسميها: find_names_starting_with_and_of_length(letter, length). ومع ذلك، هناك طريقة أفضل.

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

المزيد من المعلمات والوسيطات

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

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

يمكنك تعديل الدوال الخاصة بك على النحو التالي:

# list_of_names = [...]

def find_names_starting_with(letter, names):
    result = []
    for name in names:
        if name[0] == letter:
            result.append(name)

    return result

def find_names_of_length(length, names):
    result = []
    for name in names:
        if len(name) == length:
            result.append(name)

    return result

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

نظرًا لأنه ضمن تعريفات الدوال، لم يعد اسم القائمة التي تحتوي على الأسماء هو list_of_names ولكنه أصبح المعلمة names، ستحتاج إلى تعديل عبارات for أيضًا.

عند استدعاء find_names_starting_with ()، سيتعين عليك تمرير سلسلة بالحرف الذي تريد التصفية به وقائمة الأسماء التي تريد استخدامها. باستخدام find_names_of_length ()، تكون الوسيطتان هما الطول المطلوب وقائمة الأسماء المراد استخدامها.

يمكنك الآن استدعاء الدوال وطباعة القوائم التي ترجعها كل دالة:

# list_of_names = [...]

def find_names_starting_with(letter, names):
    result = []
    for name in names:
        if name[0] == letter:
            result.append(name)

    return result

def find_names_of_length(length, names):
    result = []
    for name in names:
        if len(name) == length:
            result.append(name)

    return result

names_p = find_names_starting_with("P", list_of_names)
print(names_p)

names_9 = find_names_of_length(9, list_of_names)
print(names_9)

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

دعونا نحاول إيجاد جميع الأسماء التي تبدأ بحرف E والتي تتكون من ستة أحرف. يمكنك أولاً استدعاء الدالة find_names_starting_with() بالطريقة نفسها كما فعلت سابقًا:

# list_of_names = [...]

def find_names_starting_with(letter, names):
    result = []
    for name in names:
        if name[0] == letter:
            result.append(name)

    return result

def find_names_of_length(length, names):
    result = []
    for name in names:
        if len(name) == length:
            result.append(name)

    return result

names_e = find_names_starting_with("E", list_of_names)
print(names_e)

يتم عرض جميع الأسماء التي تبدأ بحرف E:

['Emily', 'Evie', 'Ella', 'Eva', 'Evelyn', 'Elsie', 'Elizabeth', 'Erin', 'Ellie', 'Eliza', 'Esme', 'Emilia', 'Emma', 'Eleanor', 'Ethan', 'Edward', 'Elijah', 'Eric', 'Elliott', 'Elliot', 'Ellis', 'Ekraj']

يمكنك الآن استدعاء دالة find_names_of_length (). ومع ذلك، بدلاً من تمرير القائمة الكاملة كوسيطة، يمكنك تمرير names_e. هنا أنت تستخدم نتيجة إحدى الدوال كمدخل للدالة التالية:

# list_of_names = [...]

def find_names_starting_with(letter, names):
    result = []
    for name in names:
        if name[0] == letter:
            result.append(name)

    return result

def find_names_of_length(length, names):
    result = []
    for name in names:
        if len(name) == length:
            result.append(name)

    return result

names_e = find_names_starting_with("E", list_of_names)
names_e_and_6 = find_names_of_length(6, names_e)

print(names_e_and_6)

يعرض الإخراج قائمة بجميع الأسماء التي تبدأ بالحرف E والتي تتكون من ستة أحرف:

['Evelyn', 'Emilia', 'Edward', 'Elijah', 'Elliot']

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

رسائل الخطأ عند استدعاء الدوال

يبدو الآن توقيع الدالة find_names_starting_with () كما يلي:

def find_names_starting_with(letter, names):

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

names_e = find_names_starting_with()

يعطي هذا السطر رسائل الخطأ التالية:

Traceback (most recent call last):
  File "<path>/<filename>.py", line 1, in <module>
    names_e = find_names_starting_with()
TypeError: find_names_starting_with() missing 2 required positional arguments: 'letter' and 'names'

ستحصل على خطأ مشابه إذا مررت بإحدى الوسيطتين المطلوبتين فقط:

names_e = find_names_starting_with("E")

رسالة الخطأ تشكو الآن من الوسيطة 1 المفقودة:

Traceback (most recent call last):
  File "<path>/<filename>.py", line 1, in <module>
    names_e = find_names_starting_with("E")
TypeError: find_names_starting_with() missing 1 required positional argument: 'names'

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

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

names_e = find_names_starting_with("E", list_of_names)

السلسلة “E” هي الوسيطة الأولى، وبالتالي يتم تخصيصها للمعلمة letter. المتغير list_of_names هو الوسيط الثاني، ومحتويات هذا الصندوق مخصص للمعلمة names.

ومع ذلك، يمكنك اختيار تسمية الوسيطات في استدعاء دالة:

names_e = find_names_starting_with(letter="E", names=list_of_names)

هذه تسمى وسيطات الكلمات الأساسية keyword arguments. أنت تستخدم اسم المعلمة لتحديد الوسيطات. عند استخدام وسيطات الكلمات الأساسية، فإن الترتيب الذي تقوم بتضمين الوسيطات به في استدعاء دالة ليس مهمًا:

names_e = find_names_starting_with(names=list_of_names, letter="E")

يعمل استدعاء الدالة هذا بشكل مثالي على الرغم من أن ترتيب الوسيطات ليس هو نفسه ترتيب المعلمات في توقيع الدالة.

يمكنك استخدام مزيج من الوسيطات الموضعية ووسيطات الكلمات الرئيسية. ومع ذلك، يجب أن تأتي الوسيطات الموضعية أولاً. السطر التالي صالح:

names_e = find_names_starting_with("E", names=list_of_names)

الوسيطة “E” هي وسيطة موضعية، بينما يتم تمرير list_of_names كوسيطة كلمة أساسية. ومع ذلك، إذا حاولت تمرير وسيطة كلمة أساسية أولاً متبوعةً بوسيطة موضعية، فسوف تحصل على خطأ:

names_e = find_names_starting_with(letter="E", list_of_names)

رسالة الخطأ هي التالية:

File "<path>/<filename>.py", line 1
    names_e = find_names_starting_with(letter="E", list_of_names)
                                                   ^
SyntaxError: positional argument follows keyword argument

SyntaxError: الوسيطة الموضعية تتبع وسيطة الكلمة الأساسية (أي أنها لم تأتِ أولاً).

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

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

إعادة النظر في الغرفة البيضاء

قدم لك الفصل السابق تشبيه الغرفة البيضاء. تمثل الغرفة البيضاء برنامج الكمبيوتر. هناك مجموعة من الأرفف تشمل:

  • الكتيب “المضمّن” الذي يحتوي على جميع الدوال والكلمات الرئيسية التي يمكن لكل برنامج الوصول إليها تلقائيًا.
  • الكتب التي تم إحضارها من المكتبة باستخدام كلمة الاستيراد import
  • صناديق مع تسميات وبعض المحتويات داخل الصناديق. هذه الصناديق تمثّل المتغيرات

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

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

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

ترتيب ما يحدث عند استدعاء دالة

  • يغادر البرنامج الغرفة البيضاء ويمر عبر الباب المؤدي إلى غرفة الدالة
  • يقوم بأي إجراءات مطلوبة في غرفة الدالة
  • يعود البرنامج إلى الغرفة البيضاء الرئيسية عندما يكمل هذه المهام. يغلق باب غرفة الدالة عند الخروج

دعنا نستخدم أحد تعريفات الدوال من مشروع Finding Names لتطوير هذا السيناريو بشكل أكبر:

def find_names_starting_with(letter, names):
    result = []
    for name in names:
        if name[0] == letter:
            result.append(name)

    return result

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

المعلمات والوسيطات

عند مدخل غرفة الدالة، يوجد صندوقان فارغان جاهزان للاستخدام. لقد تم تسميتهم بالأسماء letter  و names. المعلمات عبارة عن صناديق تخزين فارغة جاهزة للتعبئة بمجرد دخول البرنامج إلى غرفة الدالة.

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

names_e = find_names_starting_with("E", list_of_names)

يؤدي استدعاء الدالة هذا إلى الخطوات التالية:

  1. يعثر البرنامج على باب يسمى find_names_starting_with يؤدي إلى غرفة الدالة.
  2. “يلتقط” سلسلة str  تحتوي على الحرف “E”.
  3. يحدد الصندوق list_of_names الذي يجده في الغرفة البيضاء. لا يأخذ الصندوق ولكن محتوياته فقط.
  4. ينتقل البرنامج بعد ذلك عبر الباب المسمى find_names_starting_with إلى غرفة الدالة، مع أخذ المعلومات التي تم جمعها معه.
  5. بمجرد دخول البرنامج إلى غرفة الدالة، سيجد صندوقي المعلمات الفارغين في انتظار ملئهما. يتم وضع البيانات التي يأخذها البرنامج إلى غرفة الدالة في هذه الصناديق

جملة الإرجاع

لقد رأيت كيف تكون المتغيرات التي تم إنشاؤها داخل الدالة محلية بالنسبة للدالة. الصندوق موجود فقط في غرفة الدالة.

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

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

ومع ذلك، فإن استدعاء الدالة في البرنامج الرئيسي يحتوي على بيان التخصيص:

names_e = find_names_starting_with("E", list_of_names)

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

عندما تنتهي إحدى الدوال من جميع أعمالها:

  • تقوم بجمع البيانات من الصناديق المسماة في بيان الإرجاع return 
  • تغادر غرفة الدالة وتأخذ البيانات معها إلى الغرفة البيضاء
  • بمجرد دخولها إلى الغرفة البيضاء، ينشئ البرنامج صندوقًا جديدًا ويخزن البيانات التي جلبها من الدالة في هذا الصندوق

حتى الدوال التي لم تكتبها بنفسك هي غرف دوال التي يمكن لبرنامجك الوصول إليها. عندما تكتب print ()، يجد البرنامج مرجعًا لهذه الدالة في الكتيب “المضمن”. يوفّر الكتيّب خريطة لموقع غرفة دالة الطباعة print داخل مدينة بايثون. سيغادر البرنامج الغرفة البيضاء، ويذهب في نزهة عبر مدينة بايثون حتى يصل إلى غرفة دالة الطباعة print، ويقوم بالإجراءات المطلوبة، ثم يعود إلى الغرفة البيضاء.

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

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

الخلاصة

لقد غطيت في هذا الفصل:

  • كيف يمكن تصنيف الدوال في البرنامج على أنها تقوم بعمليات التخزين والتكرار واتخاذ القرار وإعادة الاستخدام
  • كيف تُنشئ أو تعرّف الدوال الخاصة بك
  • ما هو الفرق بين المتغيرات المحلية والعالمية
  • كيفية إرجاع البيانات من دالة
  • كيفية تضمين المعلمات عند إنشاء دالة
  • كيفية استخدام الوسيطات الموضعية ووسيطات الكلمات الرئيسية

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

المصدر

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