تعريف دالة بايثون الخاصة بك

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

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

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

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

التعرف على الدوال في بايثون

في الرياضيات، الدالة هي علاقة أو تطابق بين مُدخل واحد أو أكثر ومجموعة من المُخرجات. يُمثَّل هذا المفهوم عادةً بالمعادلة التالية:

هنا، ()f هي دالة تعمل على المتغيرين x وy، وتُنتج نتيجة تُنسب إلى z. يمكنك قراءة هذه الصيغة على أنها z دالة لـ x وy. ولكن كيف يعمل هذا عمليًا؟ على سبيل المثال، لنفترض أن لديك دالة ملموسة تبدو كالتالي:

لنفترض الآن أن x يساوي 4 وy يساوي 2. يمكنك إيجاد قيمة z بحساب الدالة. بمعنى آخر، بجمع 4 + 2، نحصل على 6. هذا كل شيء!

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

تحتوي العديد من لغات البرمجة على دوال مدمجة، وبايثون ليس استثناءً. على سبيل المثال، تأخذ دالة )id المدمجة في بايثون كائنًا كمعامل وتُرجع مُعرّفه الفريد:

>>> language = "Python"
>>> id(language)
4390667816

يُعرّف الرقم الصحيح في هذا المُخرَج كائن السلسلة النصية الذي استخدمته كوسيطة بشكل فريد. في CPython، يُمثِّل هذا الرقم عنوان الذاكرة حيث يُخزَّن الكائن.

لاحظ مدى تشابه هذا الترميز مع ما تجده في الرياضيات. في هذا المثال، تُعادل اللغة x أو y، ويستدعي زوج الأقواس الدالة لتشغيل شيفرتها، وهو أمرٌ يُشبه تقييم دالة رياضية. ستتعلم المزيد عن استدعاء دوال بايثون لاحقًا.

على نحو مماثل، تأخذ الدالة المضمنة ()len مجموعة بيانات كحجة وتعيد طولها:

>>> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> items = len(numbers)
>>> items
10

في هذا المثال، تحتوي قائمة الأرقام على عشر قيم، لذا تقوم الدالة len() بإرجاع 10. يمكنك تعيين هذه القيمة إلى متغير items، وهو ما يعادل z في المعادلة الرياضية التي رأيتها من قبل.

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

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

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

استكشاف بعض فوائد الوال

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

التجريد

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

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

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

التغليف

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

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

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

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

النمطية

النمطية مبدأ تصميمي يُقسّم البرنامج إلى مكونات منفصلة، ​​مستقلة، وقابلة للتبديل. كل مكون هو وحدة مستقلة تُغلّف جزءًا محددًا من وظائف البرنامج.

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

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

# Main program
# Read the file
<statement_0>
<statement_1>
...
<statement_n>
# Process the file
<statement_0>
<statement_1>
...
<statement_n>
# Write the result to another file
<statement_0>
<statement_1>
...
<statement_n>

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

وبدلاً من ذلك، يمكنك هيكلة الكود كما هو موضح أدناه:

def read_file():
    <statement_0>
    <statement_1>
    ...
    <statement_n>
def process_file():
    <statement_0>
    <statement_1>
    ...
    <statement_n>
def write_file():
    <statement_0>
    <statement_1>
    ...
    <statement_n>
# Main program
read_file()
process_file()
write_file()

في هذا المثال، تُقسّم الشيفرة البرمجية إلى وحدات. فبدلاً من تجميعها معًا، تُقسّم إلى دوال منفصلة، ​​تُركّز كلٌّ منها على مهمة مُحدّدة. هذه المهام هي القراءة، والمعالجة، والكتابة. يقوم البرنامج الرئيسي الآن باستدعاء كلٍّ من هذه الدوال بالترتيب.

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

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

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

لنفترض أنك كتبتَ شيفرةً برمجيةً تؤدي وظيفةً مفيدة. ومع استمرارك في التطوير، ستجد أن الشيفرة البرمجية تُنفّذ مهمةً غالبًا ما تحتاجها في مواقع مختلفة داخل برنامجك. ماذا تفعل؟

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

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

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

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

يُعد مبدأ “عدم التكرار” (DRY) في تطوير البرمجيات حافزًا قويًا لاستخدام الدوال. فهي تُساعد على التخلص من الأكواد المكررة، كما أنها تُتيح التوافق مع مبادئ هندسة البرمجيات الأساسية الأخرى، مثل مبدأ المسؤولية الفردية (SRP).

إمكانية الصيانة

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

تساعد الدوال في صيانة الكود لأنها تسمح لك بالقيام بما يلي:

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

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

قابلية الاختبار

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

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

تعريف الدوال في بايثون

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

def function_name([parameters]):
    <block>

فيما يلي تفصيل لمكونات هذا البناء النحوي:

المكونالوصف
defالكلمة الأساسية التي تبدأ تعريف الدالة
function_nameمعرف بايثون صالح يسمي الدالة
[parameters]قائمة اختيارية مفصولة بفاصلة من المعلمات الرسمية للدالة
:علامة الترقيم التي تشير إلى نهاية رأس الدالة
<block>كتلة كود الدالة

العنصر الأخير، ، يُسمى عادةً جسم الدالة. الجسم عبارة عن سلسلة من العبارات التي تُنفَّذ عند استدعاء الدالة. يُعرَّف الجسم بالمسافة البادئة. وهذا يُشبه كتل التعليمات البرمجية المرتبطة ببنية التحكم، مثل عبارات if أو while.

هذه هي أول دالة Python الخاصة بك:

>>> def greet(name):
...     print(f"Hello, {name}!")
...
>>> greet("Pythonista")
Hello, Pythonista!

ابدأ تعريف الدالة بالكلمة المفتاحية def. ثم لديك اسم الدالة، “greet”، متبوعًا بقوسين يحيطان بالمعامل “name”. لإغلاق رأس الدالة، استخدم النقطتين (:).

بعد ذلك، لديك كتلة شيفرة مُسطرة تتكون من استدعاء دالة ()print المُدمجة. سيعرض هذا الاستدعاء رسالة ترحيب على الشاشة. أخيرًا، يمكنك استدعاء الدالة باستخدام “Pythonista” كمُعامل. ونتيجةً لذلك، ستظهر لك عبارة “Hello, Pythonista!” مطبوعةً على الشاشة.

استدعاء الدوال في بايثون

لقد قمتَ باستدعاء بعض الدوال حتى الآن. يتكون بناء الجملة من كتابة اسم الدالة متبوعًا بزوج من الأقواس، والذي يُحيط بسلسلة اختيارية من الوسائط:

function_name([arguments])

هنا، تُمثل <arguments> القيم المحددة التي تُمررها إلى الدالة. وهي تُطابق <parameters>  في تعريف دالة بايثون.

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

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

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

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

الحجج الموضعية

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

في تعريف الدالة، تُحدِّد سلسلة من المعلمات مفصولة بفواصل داخل الأقواس. على سبيل المثال، لننظر إلى الدالة التالية التي تحسب تكلفة منتج وتعرض رسالة على الشاشة:

>>> def calculate_cost(item, quantity, price):
...     print(f"{quantity} {item} cost ${quantity * price:.2f}")
...

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

>>> calculate_cost("bananas", 6, 0.74)
6 bananas cost $4.44

تُشبه المعلمات item وquantity وprice المتغيرات المُعرّفة محليًا في الدالة. عند استدعاء الدالة، تُربط القيم “bananas” و6 و0.74 بالمعلمات بالترتيب، كما لو كان ذلك بتعيين متغير:

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

>>> calculate_cost("bananas", 0.74, 6)
0.74 bananas cost $4.44
>>> calculate_cost(6, "bananas", 0.74)
Traceback (most recent call last):
    ...
TypeError: can't multiply sequence by non-int of type 'float'

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

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

>>> # Too few arguments
>>> calculate_cost("bananas", 6)
Traceback (most recent call last):
    ...
TypeError: calculate_cost() missing 1 required positional argument: 'price'
>>> # Too many arguments
>>> calculate_cost("bananas", 6, 0.74, "mango")
Traceback (most recent call last):
    ...
TypeError: calculate_cost() takes 3 positional arguments but 4 were given

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

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

update_product(1234, 15, 2.55, "12-31-2025")

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

حجج الكلمات الرئيسية

عند استدعاء دالة، يمكنك تحديد الوسيطات بالشكل argument=value. تُعرف هذه الطريقة لتمرير الوسيطات إلى دالة بايثون باستخدام الوسيطات المفتاحية. على سبيل المثال، يمكنك استدعاء دالة ()calculate_cost كما هو موضح أدناه:

>>> calculate_cost(item="bananas", quantity=6, price=0.74)
6 bananas cost $4.44

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

>>> calculate_cost(price=0.74, quantity=6, item="bananas")
6 bananas cost $4.44

إذا استخدمتَ وسيطات الكلمات المفتاحية في استدعاء دالة، فإن بايثون يُتيح لك تحديد أي وسيطة تُرسَل إلى أي مُعامل. لذا، لن يكون لديكَ أيُّ قيدٍ بتقديم الوسيطات بترتيبٍ صارم.

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

>>> calculate_cost(item="bananas", quantity=6)
Traceback (most recent call last):
    ...
TypeError: calculate_cost() missing 1 required positional argument: 'price'

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

الميزة الثانية لمعاملات الكلمات المفتاحية هي سهولة القراءة. هل تتذكر دالة ()update_product من القسم السابق؟ تعرّف على كيفية ظهورها باستخدام معاملات الكلمات المفتاحية في الاستدعاء:

# Using positional arguments
update_product(1234, 15, 2.55, "12-31-2025")
# Using keyword arguments
update_product(
    product_id=1234,
    quantity=15,
    price=2.55,
    expiration_date="12-31-2025"
)

أليست استدعاءات الكلمات المفتاحية أسهل قراءةً من استدعاءات المواضع؟ عند استدعاء الدوال في شيفرتك، إذا كان هناك احتمالٌ للغموض، فحاول استخدام الكلمات المفتاحية إن أمكن. هذه التفاصيل الصغيرة ستُحسّن من سهولة قراءة شيفرتك.

يمكنك أيضًا استدعاء دالة باستخدام كل من الوسائط الموضعية والكلمات الأساسية:

>>> calculate_cost("bananas", quantity=6, price=0.74)
6 bananas cost $4.44

في هذا المثال، قدّمتَ العنصر كوسيطة موضعية. في المقابل، الكمية والسعر هما وسيطتان للكلمات المفتاحية.

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

>>> calculate_cost(item="bananas", quantity=6, 0.74)
  File "&lt;input>", line 1
    calculate_cost(item="bananas", quantity=6, 0.74)
                                                   ^
SyntaxError: positional argument follows keyword argument

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

أخيرًا، يؤدي الإشارة إلى كلمة رئيسية لا تتطابق مع أي من المعلمات المعلنة إلى إنشاء استثناء:

>>> calculate_cost(item="bananas", quantity=6, cost=0.74)
Traceback (most recent call last):
    ...
TypeError: calculate_cost() got an unexpected keyword argument 'cost'

في هذا المثال، استخدمتَ cost كحجة مفتاحية. هذه الحجة لا تتطابق مع أيٍّ من معلمات تعريف الدالة، لذا ستحصل على استثناء TypeError.

العودة من الدوال

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

بشكل عام، يمكن للدوال القيام بواحد أو أكثر من المهام التالية:

  • تسبب تأثيرًا جانبيًا على بيئتها
  • إرجاع القيمة إلى مستدعيها
  • تسبب في تأثير جانبي وإرجاع قيمة

على سبيل المثال، الدالة التي تطبع البيانات على الشاشة فقط تُسبب تأثيرًا جانبيًا على بيئتها. هذا هو الحال مع دالة ()calculate_cost في المثال السابق.

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

في الأقسام التالية، ستتعلم كيفية كتابة دوال Python التي تسبب آثارًا جانبية ودوال تقوم بإرجاع قيم ملموسة إلى المتصل.

إنتاج الآثار الجانبية

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

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

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

>>> def double(numbers):
...     for i, _ in enumerate(numbers):
...         numbers[i] *= 2
...
>>> numbers = [1, 2, 3, 4, 5]
>>> double(numbers)
>>> numbers
[2, 4, 6, 8, 10]

تعمل هذه الدالة عن طريق توليد تأثير جانبي. تأخذ هذه الدالة قائمة أرقام، وتتكرر عليها باستخدام حلقة for، وتحصل على فهرس كل قيمة باستخدام دالة ()enumerate، ثم تُعدِّل قائمة الإدخال باستبدال كل قيمة بقيمتها المزدوجة. لقد أكملتَ مهمتك.

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

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

>>> def double(numbers):
...     result = []
...     for number in numbers:
...         result.append(number * 2)
...     return result
...
>>> numbers = [1, 2, 3, 4, 5]
>>> double_numbers = double(numbers)
>>> double_numbers
[2, 4, 6, 8, 10]
>>> numbers
[1, 2, 3, 4, 5]

الآن، تأخذ دالتك قائمة أرقام وتُنشئ قائمة جديدة من الأرقام المزدوجة. لاحظ أن البيانات الأصلية لم تتغير. لقد أزلتَ التأثير الجانبي غير المرغوب فيه.

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

إرجاع القيم

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

  • إعادة البيانات إلى المستدعي
  • إنهاء الدالة وتمرير التحكم في التنفيذ مرة أخرى إلى المتصل

إذا وضعت عبارة return متبوعة بتعبير داخل دالة Python، فستحصل بيئة الاستدعاء على قيمة هذا التعبير مرة أخرى:

>>> def identity(x):
...    return x
...
>>> x = identity(42)
>>> x
42

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

def function_name():
    return [expression_0[, expression_1, ..., expression_n]]

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

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

>>> def create_point(x, y):
...     return x, y
...
>>> create_point(2, 4)
(2, 4)

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

ملاحظة: يمكنك استخدام عدة عبارات return في دالة واحدة. ستتعلم المزيد عن هذا خلال هذا البرنامج التعليمي.

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

على سبيل المثال، في هذا الكود، تُرجع الدالة ()as_dict قاموسًا. في بيئة الاستدعاء، يُرجع استدعاء هذه الدالة قاموسًا، وجملة مثل as_dict()[“key”] صحيحة تمامًا:

>>> def as_dict():
...     return dict(one=1, two=2, three=3)
...
>>> as_dict()
{'one': 1, 'two': 2, 'three': 3}
>>> as_dict()["two"]
2

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

يمكنك أيضًا استخدام return فارغ بدون تعبير في النهاية. في هذه الحالة، ستعيد الدالة القيمة “None”:

>>> def function():
...     return
...
>>> print(function())
None

هنا، قيمة الإرجاع هي None افتراضيًا. لاحظ أن جلسات Python REPL لا تُظهر None عند استدعاء دالة تُرجع هذا الكائن. لهذا السبب، عليك استخدام ()print في هذا المثال.

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

>>> def find_user(username, user_list):
...     for user in user_list:
...         if user["username"] == username:
...             return user
...     return None
...
>>> users = [
...     {"username": "alice", "email": "alice@example.com"},
...     {"username": "bob", "email": "bob@example.com"},
... ]
>>> find_user("alice", users)
{'username': 'alice', 'email': 'alice@example.com'}
>>> print(find_user("linda", users))
None

مع أن return None” صراحةً ليس إلزاميًا، إلا أنه حل مناسب وسهل في هذا المثال. إذا حصلت على “لا شيء”، فذلك لأنه لم يتم العثور على المستخدم.

يقوم Python بإرجاع None تلقائيًا من دالة لا تتضمن عبارات إرجاع صريحة:

>>> def function():
...     pass
...
>>> print(function())
None

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

يمكنك استخدام دوال تُرجع قيمة “None” صراحةً في سياق منطقي. في هذه الحالة، ستكون طريقة بايثون للتحقق من قيمة return كما هو موضح أدناه:

>>> if find_user("linda", users) is None:
...     print("Linda isn't a registered user")
...
Linda isn't a registered user

قارن قيمة return الدالة مع “None” باستخدام عامل “is”. يمكنك أيضًا استخدام عامل “is not” في الحالات المناسبة.

على سبيل المثال، إذا كنت تريد إجراء بعض المعالجة الإضافية لبيانات المستخدم، فيمكنك القيام بشيء مثل التالي:

>>> if find_user("alice", users) is not None:
...     print("Do something with Alice's data...")
...
Do something with Alice's data...

في بايثون، إذا كان لديك شرطٌ يُمكن تقييمه إلى “None”، فعليك مقارنته صراحةً بهذا الكائن باستخدام عاملي “is” و”is not”. هذا يُوضّح أنك تتحقق تحديدًا من عدم وجود قيمة، مُتجنبًا الالتباس مع قيم خاطئة أخرى مثل “0” أو “” أو “False”.

إنهاء الوظائف مبكرًا

تؤدي عبارة return إلى خروج بايثون من الدالة فورًا وإعادة التحكم في التنفيذ إلى المُستدعي. يتيح لك هذا السلوك استخدام عبارة return عادية عند الحاجة إلى إنهاء تنفيذ الدالة مبكرًا.

فكر في مثال اللعبة التالي الذي يعالج ملفًا:

from pathlib import Path
def read_file_contents(file_path):
    path = Path(file_path)
    if not path.exists():
        print(f"Error: The file '{file_path}' does not exist.")
        return
    if not path.is_file():
        print(f"Error: '{file_path}' is not a file.")
        return
    return path.read_text(encoding="utf-8")

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

إرجاع القيم المنطقية

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

على سبيل المثال، إليك دالة تتحقق مما إذا كان الرقم زوجيًا:

>>> def is_even(number):
...     return number % 2 == 0
...
>>> is_even(2)
True
>>> is_even(3)
False

في هذا المثال، تأتي قيمة return من شرط يتم تقييمه إما بـ True أو False، اعتمادًا على رقم الإدخال.

دوال المسند شائعة ومفيدة في البرمجة. على سبيل المثال، دوال بايثون المدمجة ()all و()any و()hasattr و)isinstance و()issubclass كلها دوال مسندة.

تعريف واستدعاء الدوال: الميزات المتقدمة

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

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

قيم الوسيطة الافتراضية

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

فيما يلي مثال سريع لتعريف دالة بقيمة وسيطة افتراضية:

>>> def greet(name="World"):
...     print(f"Hello, {name}!")
...
>>> greet()
Hello, World!
>>> greet("Pythonista")
Hello, Pythonista!

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

تُستخدم كائنات مثل None وTrue وFalse عادةً كقيم وسيطات افتراضية في دوال بايثون. تُفيد القيم المنطقية عندما تحتوي دوالك على وسيطات تعمل كعلامات.

خذ بعين الاعتبار المثال التالي:

>>> def greet(name, verbose=False):
...     if verbose:
...         print(f"Hello, {name}! Welcome to pyarabic!")
...     else:
...         print(f"Hello, {name}!")
...
>>> greet("Pythonista", verbose=True)
Hello, Pythonista! Welcome to pyarabic!
>>> greet("Pythonista")
Hello, Pythonista!

في هذا التطبيق الجديد لـ ()greet، تُعرّف الاسم كمعامل موضعي إلزامي. على العكس، يُعد verbose علامة اختيارية تُعيَّن افتراضيًا على القيمة False. عند استدعاء الدالة، يمكنك ضبط verbose على القيمة True للحصول على تحية أطول، أو تجاهل هذه المعلمة للحصول على تحية أقصر.

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

>>> def append_to(item, target=[]):
...     target.append(item)
...     return target
...

هذه دالة توضيحية فقط. تُضيف item إلى نهاية target، والذي يكون افتراضيًا قائمة فارغة. للوهلة الأولى، قد يبدو أن استدعاءات ()append_to المتتالية تُعيد قوائم عناصر مفردة، كما في الكود الافتراضي التالي:

>>> append_to(1)
[1]
>>> append_to(2)
[2]
>>> append_to(3)
[3]

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

>>> append_to(1)
[1]
>>> append_to(2)
[1, 2]
>>> append_to(3)
[1, 2, 3]

تتذكر قائمة target البيانات بين الاستدعاءات. يحدث هذا لأنك تستخدم نفس كائن القائمة الذي يظهر كقيمة افتراضية في تعريف الدالة.

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

>>> def append_to(item, target=None):
...     if target is None:
...         target = []
...     target.append(item)
...     return target
...
>>> append_to(1)
[1]
>>> append_to(2)
[2]
>>> append_to(3)
[3]

الآن، يصبح target افتراضيًا “None” بدلًا من قائمة فارغة. ثم، تحقق مما إذا كان المتغير “None”. في هذه الحالة، أنشئ قائمة فارغة جديدة محلية للدالة.

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

عدد متغير من الوسائط الموضعية: args*

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

فيما يلي عرض سريع لكيفية عمل هذا النحو:

>>> def function(*args):
...     print(args)
...
>>> function(1, 2, 3, 4, 5)
(1, 2, 3, 4, 5)

في تعريف الدالة، تستخدم صيغة args*. عند استدعاء هذه الدالة بعدد عشوائي من الوسائط، يقوم بايثون بجمعها في مجموعة.

لتوضيح ذلك بمثال أكثر واقعية، لنفترض أنك تريد تطبيق دالة ()average التي يمكنها أخذ أي عدد من الوسائط الموضعية وحساب متوسطها. يمكنك القيام بذلك باستخدام args*:

>>> def average(*args):
...     return sum(args) / len(args)
...
>>> average(1, 2, 3, 4, 5, 6)
3.5
>>> average(1, 2, 3, 4, 5, 6, 7)
4.0
>>> average(1, 2, 3, 4, 5, 6, 7, 8)
4.5

هذه الدالة مرنة للغاية، مما يسمح لك بحساب متوسط ​​عدد عشوائي من قيم الإدخال. ولأن args عبارة عن مجموعة، يمكنك استخدام الدالة ()len لتحديد عدد الوسائط المُدخلة وحساب المتوسط ​​بسرعة ودقة.

لاحظ أن هذا التنفيذ لـ ()average يتجاهل بعض الحالات الشاذة، مثل استدعاء الدالة بدون وسيطات. إنه مجرد عرض توضيحي سريع لكيفية استخدام args* في حالة استخدام واقعية.

عدد متغير من وسيطات الكلمات الرئيسية: kwargs**

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

يؤدي وضع علامة النجمة المزدوجة (**) قبل اسم المعلمة في تعريف الدالة إلى إخبار Python بتجميع وسيطات الكلمات الأساسية المقدمة في القاموس:

>>> def function(**kwargs):
...     print(kwargs)
...
>>> function(one=1, two=2, three=3)
{'one': 1, 'two': 2, 'three': 3}

في هذه الحالة، تُجمّع بايثون الكلمات المفتاحية one=1، وtwo=2، وthree=3 في قاموس. بعد ذلك، يُمكن استخدام قاموس kwargs في نص الدالة كقاموس عادي.

مرة أخرى، يمكنك استخدام أي اسم، لكن مصطلح “كوارغز” (kwargs) المميز – وهو اختصار لـ “وسيطات الكلمات المفتاحية” – شائع الاستخدام وأصبح معيارًا. ليس عليك الالتزام به، ولكن إن التزمت به، فسيفهم أي شخص مُلِمٌّ بقواعد بايثون ما تقصده.

من المهم ملاحظة أنه لكي تعمل هذه الميزة، يجب استخدام مُعرِّفات بايثون صالحة لكلمات المفتاح. لا يمكنك استخدام مفاتيح قاموس صالحة، ولكن مُعرِّفات غير صالحة، مثل 1=”one”.

إليك مثال عملي على استخدام kwargs**. لنفترض أنك تريد كتابة دالة لإنشاء تقرير، ولكنك لا تعرف مسبقًا الحقول التي سيتم تمريرها:

>>> def report(**kwargs):
...     print("Report:")
...     for key, value in kwargs.items():
...         print(f" - {key.capitalize()}: {value}")
...
>>> report(name="Keyboard", price=19.99, quantity=5, category="PC Components")
Report:
 - Name: Keyboard
 - Price: 19.99
 - Quantity: 5
 - Category: PC Components

في هذا المثال، تستخدم دالة ()report صيغة kwargs** لقبول عدد عشوائي من وسيطات الكلمات المفتاحية. كائن kwargs عبارة عن قاموس، لذا يمكنك التكرار عليه كما تفعل مع أي قاموس عادي. يمكنك الاستفادة من هذه الخاصية لإنشاء تقرير منسق باستخدام بيانات الإدخال.

مرة أخرى، عليك أن تدرك أن ()report لا يأخذ في الاعتبار الحالة الهامشية لاستدعاء الدالة بدون وسيطات.

استكشاف الميزات الاختيارية للدوال

يتيح لك Python إضافة سلاسل توثيقية وتعليقات توضيحية اختيارية إلى دوالك.

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

في هذا القسم، ستتعلم كيفية إضافة واستخدام كلتا الميزتين الاختياريتين ولكن القيمتين للغاية في دوال Python الخاصة بك.

سلاسل الوثائق

عندما تكون العبارة الأولى في نص الدالة عبارة عن سلسلة نصية، يُعرف ذلك بسلسلة توثيق الدالة.

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

فيما يلي ()average الذي يظهر سلسلة توثيقية بأسلوب Google مع أمثلة اختبار doctest:

def average(*args):
    """Calculate the average of given numbers.
    Args:
        *args (float or int): One or more numeric values.
    Returns:
        float: The arithmetic mean of the provided values.
    Raises:
        ZeroDivisionError: If no arguments are provided.
    Examples:
        >>> average(10, 20, 30)
        20.0
        >>> average(5, 15)
        10.0
        >>> average(7)
        7.0
    """
    return sum(args) / len(args)

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

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

ملاحظة: تم تفصيل تنسيق Docstring والاتفاقيات الدلالية في PEP 257.

في المثال أعلاه، استخدمتَ نمط سلسلة توثيق جوجل، الذي يتضمن وصفًا لمعاملات الدالة وقيمة return. كما استخدمتَ أمثلة اختبار توثيق. تُحاكي هذه الأمثلة جلسة REPL، وتتيح لكَ تقديم مرجع سريع لكيفية استخدام الدالة. كما يُمكن استخدامها كاختبارات يُمكنك تشغيلها باستخدام وحدة اختبار توثيق من المكتبة القياسية.

عند تعريف سلسلة توثيق، يُعيّنها بايثون إلى سمة دالة خاصة تُسمى .__doc__ يمكنك الوصول إلى سلسلة توثيق الدالة كما هو موضح أدناه:

>>> print(average.__doc__)
Calculate the average of given numbers.
    Args:
        *args (float or int): One or more numeric values.
    Returns:
        float: The arithmetic mean of the provided values.
    Raises:
        ZeroDivisionError: If no arguments are provided.
    Examples:
        >>> average(10, 20, 30)
        20.0
        >>> average(5, 15)
        10.0
        >>> average(7)
        7.0

تتيح لك السمة .__doc__ الوصول إلى سلسلة توثيق أي دالة بايثون تحتوي عليها. كبديل، يمكنك تشغيل الأمر help(function_name) لعرض سلسلة توثيق الدالة المستهدفة، وهو الإجراء المُوصى به.

يُعد تحديد سلسلة توثيقية لكل دالة بايثون تُعرّفها من أفضل ممارسات البرمجة. يمكنك اليوم الاستفادة من نماذج اللغات الكبيرة (LLMs) وروبوتات الدردشة مثل ChatGPT لتوثيق شيفرتك. مع ذلك، احرص دائمًا على مراجعة النص المُولّد وتعديله لضمان وصفه الدقيق لشيفرتك وتوافقه مع معايير مشروعك.

التعليقات التوضيحية

توفر تعليقات الدالة طريقة لإرفاق البيانات الوصفية بحجج الدالة وقيمة return.

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

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

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

def function(arg_0: &lt;type>, arg_1: &lt;type>, ..., arg_n: &lt;type>) -> &lt;type>:
    &lt;block>

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

خذ بعين الاعتبار دالة اللعبة التالية:

>>> def function(a: int, b: str) -> float:
...     print(type(a), type(b))
...     return 1, 2, 3
...
>>> function("Hello!", 123)
&lt;class 'str'> &lt;class 'int'>
(1, 2, 3)

ماذا يحدث في هذا المثال؟ تشير شروح الدالة ()function إلى أن الوسيطة الأولى يجب أن تكون عددًا صحيحًا، والوسيط الثاني سلسلة، وقيمة return عدد عشري. مع ذلك، يتجاهل استدعاء الدالة ()function جميع هذه الأنواع المتوقعة. الوسيطتان هما سلسلة وعدد صحيح على التوالي، وقيمة return هي مجموعة. ومع ذلك، يتغاضى المترجم عن كل ذلك دون أي اعتراض.

لا تفرض التعليقات التوضيحية أي قيود على الكود. إنها بيانات وصفية مُرفقة بالدالة من خلال قاموس يُسمى  .__annotations__ يتوفر هذا القاموس كسمة لكائنات الدالة:

>>> function.__annotations__
{'a': &lt;class 'int'>, 'b': &lt;class 'str'>, 'return': &lt;class 'float'>}

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

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

لتوضيح كيفية مساعدة تلميحات النوع في تحسين جودة الكود الخاص بك، لنفترض أنك كتبت الدالة التالية وتتوقع استخدامها مع الوسائط الرقمية:

>>> def add(a, b):
...     return a + b
...
>>> add(3, 4)
7

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

>>> add("3", "4")
'34'

في هذا المثال، تم استدعاء الدالة مع سلسلتين نصيتين. داخليًا، يربط عامل الجمع (+) السلسلتين وينتج سلسلة نصية جديدة، “34”. هذه ليست النتيجة المتوقعة. بالطبع، السبب هو أن الوسيطات المُقدمة ليست من نوع البيانات الصحيح، ولكن لعدم تقديم تلميحات النوع، قد يظن المستخدم أن الدالة ستعمل مع السلاسل النصية أيضًا.

وهنا النسخة التي تحمل تلميحًا بالنوع:

>>> type Number = int | float
>>> def add(a: Number, b: Number) -> Number:
...     return a + b
...

في هذا الإصدار من دالة ()add، تستخدم تلميحات النوع لتوضيح أن الدالة يجب أن تأخذ قيمًا عددية من نوع int أو float. كما تحدد أن الدالة يمكنها إرجاع عدد صحيح أو عدد عشري. لجعل الكود أكثر وضوحًا، استخدم اسمًا مستعارًا للنوع، Number، يمثل قيمًا عددية أو عشرية.

كما تعلم، لا يزال بإمكانك استدعاء هذه الدالة باستخدام وسيطتين نصيتين. مع ذلك، سيُنبهك مُدقّق النوع الثابت إلى استخدام الدالة بطريقة غير متوقعة.

غالبًا ما تُدمج مدققات الأنواع الثابتة في محررات الأكواد الحديثة وبيئات التطوير المتكاملة (IDEs). لذا، من المرجح أن يُبرز محرر النصوص لديك تلقائيًا الاستدعاء add(“3”, “4”) كعامل قد يُسبب مشاكل أثناء التشغيل.

يمكنك أيضًا تشغيل مُدقِّق نوع ثابت على الكود الخاص بك من سطر الأوامر. على سبيل المثال، لنفترض أن الكود موجود في نص برمجي كما يلي:

def add(a: int | float, b: int | float) -> int | float:
    return a + b
add("3", "4")

لتشغيل mypy على هذا الملف، الخطوة الأولى هي تثبيته في بيئتك. بعد ذلك، يمكنك تنفيذ الأمر التالي:

$ mypy calculations.py
calculations.py:4: error: Argument 1 to "add" has incompatible type "str";
⮑ expected "int | float"  [arg-type]
calculations.py:4: error: Argument 2 to "add" has incompatible type "str";
⮑ expected "int | float"  [arg-type]
Found 2 errors in 1 file (checked 1 source file)

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

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

>>> def rectangle_area(
...     width: {"description": "Width of rectangle", "type": float},
...     height: {"description": "Height of rectangle", "type": float},
... ) -> {"description": "Area of rectangle", "type": float}:
...     return width * height
...
>>> rectangle_area.__annotations__
{
    'width': {'description': 'Width of rectangle', 'type': &lt;class 'float'>},
    'height': {'description': 'Height of rectangle', 'type': &lt;class 'float'>},
    'return': {'description': 'Area of rectangle', 'type': &lt;class 'float'>}
}

في هذا المثال، تُرفق تعليقات توضيحية بمعلمات width وheight، وكذلك بقيمة إرجاع الدالة. كل تعليق توضيحي هو قاموس يحتوي على وصف ونوع البيانات المتوقع للكائن المعني.

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

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

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

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

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

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

الأسئلة الشائعة

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

هذه الأسئلة الشائعة تتعلق بأهم المفاهيم التي تناولتها في هذا البرنامج التعليمي. انقر على زر “إظهار/إخفاء” بجوار كل سؤال لعرض الإجابة.

تستخدم الدوال في Python لتعزيز إمكانية إعادة استخدام التعليمات البرمجية، والوحدات، وإمكانية الصيانة من خلال تغليف التعليمات البرمجية المتكررة في وحدة قابلة للاتصال واحدة.


اكتشاف المزيد من بايثون العربي

اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.

يمكنك تعريف دالة Python باستخدام الكلمة الأساسية def، متبوعة باسم الدالة، والأقواس مع الوسائط الاختيارية، والنقطة، وكتلة التعليمات البرمجية المسننة.


اكتشاف المزيد من بايثون العربي

اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.

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


اكتشاف المزيد من بايثون العربي

اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.

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


اكتشاف المزيد من بايثون العربي

اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.


اكتشاف المزيد من بايثون العربي

اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *

Scroll to Top

اكتشاف المزيد من بايثون العربي

اشترك الآن للاستمرار في القراءة والحصول على حق الوصول إلى الأرشيف الكامل.

Continue reading