تعريف الدوال
أنشئ كتل كود قابلة لإعادة الاستخدام باستخدام def والوسائط وقيم الإرجاع.
- تعريف الدوال واستدعاؤها باستخدام def
- استخدام الوسائط الموضعية والمفتاحية والافتراضية
- إرجاع القيم من الدوال
- كتابة سلاسل توثيق (docstrings) لتوثيق الدوال
من الصيغة إلى الآلة المسماة
تنفر الرياضياتُ من التكرار. تعلمت كـ قاعدة ، تعريفٌ واحدٌ يوظَّف ألفَ مرةٍ على ألفِ مدخلٍ مختلف:
إنّ def في بايثون هي الحركة نفسها: ربطُ اسمٍ بحسابٍ، ليستطيع أيُّ مستدعٍ تطبيقه. الدالة آلةٌ بشقوقِ مدخلٍ موسومةٍ وبابِ مخرجٍ واحد:
def add(a, b):
return a + b
result = add(3, 5) # 8الاسم، والقوسان اللذان يحملان الوسيطين ، والنقطتان اللتان تبدأان الوصفة ، هذا هو التعريف. والاستدعاء add(3, 5) تطبيقُ القاعدة عند و، تمامًا كما يطبّق قاعدةً عند .
التعريف والاستدعاء
أول دالةٍ تكتبها تغيّر العالمَ تحيةً تحية:
def greet(name):
"""Print a greeting for the given name."""
print(f"Hello, {name}!")
greet("Alice") # Hello, Alice!ثلاثةُ أجزاءٍ تستحق أسماء. الوسائط (parameters) هي المتغيرات في التعريف ، شقوقُ المدخل . الوسيطات (arguments) هي القيمُ الملموسةُ المقدَّمةُ عند موضع الاستدعاء ، المدخل . والسطرُ المحاط بعلامات اقتباس ثلاثيةٍ من الداخل هو سلسلة التوثيق: توثيقٌ يسكن جوار الكود، ليستطيع help(greet) الإجابةَ عمّا تفعله الدالة.
Return: باب المخرج
يرسل print نصًّا إلى الشاشة؛ ويسلّم return قيمةً إلى المستدعِي. التفرقةُ خفيةٌ وحاسمة:
def add(a, b):
return a + b
result = add(3, 5) # result == 8
printed = print("8") # printed هو None — print لا يعيد شيئًاكل دالةٍ بلا return تعيد None بصمت ، الآلة لا تنتج مخرجًا. وحين تريد أن يستمرّ الناتجُ الحسابيُّ لدورتك في التدفق، تذكر: return لا print.
وسائط افتراضية
بعض الوسائط لها ضبطٌ طبيعيٌّ تحتفظ به أكثرُ الاستدعاءات. أعطها قيمةً افتراضية، وليتمكّن المستدعون من تجاوزها:
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
print(greet("Alice")) # Hello, Alice!
print(greet("Bob", "Hey")) # Hey, Bob!قاعدة الترتيب صارمة: الوسائط الافتراضية تأتي بعد غير الافتراضية. def f(x, y=5) مشروعة؛ أمّا def f(x=1, y) فخطأ قواعدي، لأن بايثون تحلّ الوسيطات موضعيًّا من اليسار، والثغرةُ ستكون ملتبسة.
الوسيطات المفتاحية
يمكن أن تصل الوسيطات مسماةً أيضًا، فيشتري ذلك وضوحًا حين تكبر مجموعة الوسائط:
def create_user(name, age, role="student"):
return {"name": name, "age": age, "role": role}
user = create_user(age=25, name="Alice", role="admin")للوسيطات المسمّاة أيُّ ترتيبٍ ، اسمُ الوسيط هو ملصقُ كل طرد. والاستدعاء الذي يسمّي مدخلاته يُقرأ جملةً لا شيفرةً تحتاج فكًّا.
*args و **kwargs
وماذا إذا كان عددُ المدخلات مجهولًا مقدمًا؟ لا تعرف المجموعةُ عددَ المضافات التي ستستقبل. يجمع *args أيَّ عددٍ من الوسيطات الموضعية في مصفوفة؛ ويجمع **kwargs الوسيطات المسماة في قاموس:
def total(*args):
return sum(args)
print(total(1, 2, 3, 4)) # 10
def print_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(name="Alice", age=25)النجمةُ هي الإيماءة: تفتح * قائمةَ الوسيطات في حزمةٍ واحدة. ذلك هو الفرق بين مجموعٍ بتوقيعٍ ثابتٍ ومجموعٍ يقبل لأيِّ .
العودة المبكرة بصفتها حارسًا
بعض الأكواد تبدأ بفحص الحالة الوحيدة التي يجب ألا تمضي. استدلالاتُ صيغة «إلا إذا كان » تُعلن حارسًا في الأعلى، فتُرجِع فورًا:
def divide(a, b):
if b == 0:
return None
return a / bجملةُ الحارس تطوي زوجَ if/else إلى خطٍّ مستقيم: حالةُ الفشل تخرج مبكرًا، والمسار الصادق يجري دون تداخل.
مثالٌ محلول: الآلةُ f
تصيرُ الدالةُ التربيعيةُ التي فتحتِ الدرسَ ثلاثَ عباراتِ return:
def quad(x):
"""يردّ x² − 5x + 6."""
return x * x - 5 * x + 6
quad(2) # 0
quad(3) # 0
quad(1) # 2القاعدةُ نفسُها بثلاثِ مدخلاتٍ. تصيرُ الصيغةُ آلةً قابلةً لإعادةِ الاستخدام: تُعرَّف مرةً وتُطبَّق ألفَ مرةٍ، ويتركُ سطرُ التوثيقِ مكتوبًا أيَّ قاعدةٍ تحويها.
أخطاء شائعة
- وسائط افتراضية قابلة للتغيير. تنشئ
def f(items=[])قائمةً واحدةً مشتركةً بين كل الاستدعاءات ، تتكدس العناصر بين الاستدعاءين. اجعل الافتراضيNoneوابنِ القائمة في الداخل. - نسيان
return. كل دالةٍ بلا عودةٍ تعيدNone؛ طلبت قيمةً فتنل ظلًّا. - وسائط كثيرة جدًّا. بعد الثالثة أو الرابعة تصير الشقوقُ أُحجيةً. اجمع الوسائط المتقاربة في قاموسٍ أو dataclass.
- استدعاءُ دالةٍ مُعرَّفةٍ في الأسفل. ينفّذُ بايثون من الأعلى إلى الأسفل؛ فاستدعاءُ
f()قبل أن يبلغَ سطرُdef fالمفسِّرَ يرفعُNameError. عرّفْ ثم استدعِ.
🧩 تحديات
🧩 تحدٍّ ، فكّر أولًا ثم أظهر
اكتب is_palindrome(text) ترجع True إذا كانت السلسلة تُقرأ نفسها من الوجهين؛ تجاهل حالةَ الحروف.
💡 الجواب: def is_palindrome(text): return text.lower() == text.lower()[::-1] ، تحويلُ الحروف الصغيرة يُتماثل المقارنة، والشريحة المعكوسة [::-1] هي الصورةُ المرآتية.
🧩 تحدٍّ ، فكّر أولًا ثم أظهر
اكتب fizzbuzz(n) ترجع قائمةً من 1 إلى ، مستبدلةً مضاعفاتِ الثلاثة بـ "Fizz" ومضاعفاتِ الخمسة بـ "Buzz" ومضاعفاتِ الاثنين معًا بـ "FizzBuzz".
💡 الجواب: مضاعفاتُ الاثنين معًا هي مضاعفاتُ $\mathrm{lcm}(3,5) = 15$، فاختبر تلك الحالة أولًا: ["FizzBuzz" if i % 15 == 0 else "Fizz" if i % 3 == 0 else "Buzz" if i % 5 == 0 else i for i in range(1, n+1)].
🤔 أسئلة سقراطية
- لماذا يجب أن تأتي الوسائط الافتراضية بعد غير الافتراضية؟ وماذا ينهار لو انقلبت القاعدة؟
- ماذا يمنحك
*argsمما لا يمنحه وسيطُ قائمةٍ واحد؟ ومتى تلجأ إلى أحدهما دون الآخر؟ - كيف تقرّر بايثون أيَّ تعريفٍ تطبّق حين يوجدا معًا
def f(x)وdef f(x, y=5)؟
✅ فحص سريع
١. ماذا يرجع هذا؟ def f(x, y=3): return x + y; f(5)
٢. ما المخرج؟ def f(a, b=[]): b.append(a); return b; print(f(1)); print(f(2))