الدالة هي كتلة خطوات لها اسم
في كل مرة تجد نفسك تكرّر نفس السطور أكثر من مرة، أو تشعر برغبة في إعطاء اسم مختصر لمنطق صغير متكرر، فهذه إشارة واضحة أنك جاهز لكتابة دالة. الدوال في بايثون هي أول أداة حقيقية تساعدك على التعامل مع التعقيد وتنظيم الكود داخل برامجك.
الشكل الأساسي لتعريف دالة بايثون:
لنفكّك هذا السطر:
defهي الكلمة المفتاحية التي تبدأ بها تعريف الدالة.greetهو اسم الدالة.(name)هي قائمة المعاملات، أي المدخلات التي تستقبلها الدالة.- النقطتان الرأسيتان تُنهيان سطر الترويسة، وما يليهما من كتلة مُزاحة هو جسم الدالة.
greet("Ada")هذا استدعاء للدالة. هنا تُنفِّذ بايثون جسم الدالة بعد أن تربطnameبالقيمة"Ada".
تبقى الدوال خامدة حتى تستدعيها. مجرد تعريف الدالة لا يُشغّلها، لكن استدعاءها بـ greet("Ada") هو ما يُشغّلها فعلاً.
المعاملات والوسائط في دوال بايثون
كلمة معامل (parameter) تشير إلى الاسم المكتوب داخل تعريف الدالة، أما الوسيط (argument) فهو القيمة التي تُمرِّرها عند الاستدعاء. الفرق قد يبدو شكلياً في الحديث العادي، لكنه مفيد جداً حين تقرأ رسائل الأخطاء وتحاول فهمها.
هنا base و exponent هما المعاملان (parameters)، بينما 2 و 10 هما الوسائط (arguments) التي نمررها. بايثون تربطها بالترتيب: الوسيط الأول يذهب إلى المعامل الأول، وهكذا.
return في بايثون: إرجاع قيمة إلى المُستدعي
الفرق بين print و return مهم: print يطبع على الشاشة فقط، أما return فيُعيد قيمة فعلية إلى الكود الذي استدعى الدالة حتى يستطيع استخدامها:
بدون return، تُرجع الدالة القيمة None افتراضيًا:
return يُنهي تنفيذ الدالة فورًا أيضًا. وكثيرًا ما تصادف استخدام الـ early return للخروج من الحالات الاستثنائية قبل الوصول إلى المنطق الأساسي:
الخروج المبكر عبر return يساعدك على تجنّب التداخل العميق للجسم الرئيسي داخل شروط if/else.
الوسائط الافتراضية في بايثون
يمكنك تحديد قيمة افتراضية لأي معامل، وتُستخدم هذه القيمة عندما لا يُمرِّر المستدعي قيمة صريحة:
جميع المعاملات التي لها قيم افتراضية يجب أن تأتي بعد المعاملات التي ليس لها قيم افتراضية. مثلًا def f(a, b=1, c): يعتبر خطأ في الصياغة (syntax error).
فخ القيمة الافتراضية المتغيّرة (mutable default)
هذه من أشهر المطبّات في بايثون. تابع معي المثال التالي:
كنت تتوقع أن كل استدعاء يبدأ بقائمة فارغة، لكن ما يحصل فعلياً هو أن القائمة الافتراضية مشتركة بين جميع الاستدعاءات، فتتراكم فيها العناصر. السبب أن بايثون يُقيّم القيمة الافتراضية مرة واحدة فقط عند تعريف الدالة، ثم يُعيد استخدام نفس القائمة إلى الأبد.
الحل الآمن هو استخدام None كقيمة افتراضية وإنشاء القائمة داخل الدالة نفسها:
الآن كل استدعاء لا يُمرّر items سيحصل على قائمة جديدة. هذه القاعدة تنطبق على القوائم والقواميس والمجموعات — أي شيء قابل للتعديل (mutable).
الوسائط المسماة في بايثون
تقدر تمرّر الوسائط بالاسم بدل الاعتماد على ترتيبها. هذا الأسلوب يسمح لك بتخطّي بعض المعاملات الوسطى، ويخلّي الاستدعاءات الطويلة أوضح وأسهل في القراءة:
يمكن الجمع بين الوسائط الموضعية والوسائط المسماة في استدعاء الدالة، لكن لا بد أن تأتي الوسائط الموضعية أولًا:
عندما يتجاوز عدد معاملات الدالة ثلاثة أو أربعة، فإن استخدام الوسائط المسماة يجعل استدعاء الدالة أوضح وأسهل في القراءة بكثير.
معاملات موضعية فقط ومعاملات مسماة فقط
تتيح لك بايثون تحديد بعض المعاملات بحيث تُمرَّر عن طريق الموضع فقط (باستخدام /) أو عن طريق الاسم فقط (باستخدام *):
لا تحتاجها من أول يوم. تصير مفيدة لما تبدأ بتصميم واجهات برمجية (APIs) وتريد تتحكم في الطريقة اللي يستدعي بها المستخدم دالتك.
docstring في بايثون وتسمية الدوال
أول سطر داخل أي دالة ممكن يكون docstring — نص محاط بثلاث علامات اقتباس تعامله بايثون كتوثيق للدالة:
أدوات مثل help() وتلميحات المحرر ومولّدات التوثيق تقرأ الـ docstring تلقائيًا، لذا حتى سطر واحد أفضل بكثير من لا شيء.
وخُذ وقتك في اختيار الأسماء:
- أسماء الدوال يجب أن تكون أفعالًا:
fetch_profile,compute_total,is_valid. - استخدم صيغة
lower_snake_case. - الدوال التي تُرجع قيمة منطقية عادةً ما تبدأ بـ
is_أوhas_أوcan_.
الاسم الجيد يجعل مكان الاستدعاء واضحًا بذاته دون الحاجة إلى تعليق يشرحه.
الدوال النقية (Pure Functions) أسهل في الفهم والتتبع
الدالة النقية (pure function) هي التي تُرجع نفس النتيجة لنفس المدخلات، ولا يكون لها أي آثار جانبية؛ فهي لا تُعدّل حالة عامة، ولا تطبع إلى stdout (إلا لأغراض التصحيح)، ولا تكتب إلى ملفات.
كلا الأسلوبين له مكانه، لكن في الكود الذي تنوي إعادة استخدامه واختباره، حاول قدر الإمكان أن تميل إلى الدوال النقية (pure functions).
مثال عملي صغير
إليك دالة بسيطة تجمع بين الوسائط الافتراضية والوسائط المسماة وreturn:
التالي: قوائم الوسائط المرنة
أحياناً ما تعرفش كم وسيط راح تستقبل الدالة، أو تبغى تمرّر الوسائط الواصلة كما هي لدالة ثانية. هنا يجي دور *args و **kwargs. نشوفهم بالتفصيل في الصفحة الجاية.
الأسئلة الشائعة
كيف أُعرِّف دالة في بايثون؟
تبدأ بالكلمة المفتاحية def متبوعةً باسم الدالة، ثم قوسين للمعاملات، ثم نقطتين رأسيتين. الكتلة المُزاحة تحت السطر هي جسم الدالة. مثال: def greet(name): print(f'Hi, {name}').
ما وظيفة return في بايثون؟
return تُرجِع قيمة إلى الجهة التي استدعت الدالة. وإذا لم تضع return صريحًا، فالدالة تُرجِع None تلقائيًا. بمجرد تنفيذ return تخرج الدالة فورًا، وأي كود يأتي بعده في الكتلة نفسها لن يُنفَّذ.
ما هي الوسائط الافتراضية في بايثون؟
الوسائط الافتراضية تسمح للمعامل بأن تكون له قيمة جاهزة تُستخدم إذا لم يُمرِّر المستدعي قيمة. مثلًا def greet(name='friend'): يعني أن استدعاء greet() بدون وسائط سيستخدم 'friend'. تجنّب تمامًا القيم الافتراضية القابلة للتعديل مثل []، وراجع النمط الآمن البديل أدناه.