الدوال وإطارات الاستدعاء والنتائج المُرجعة
مُقدَّم| English | المعنى |
|---|---|
| function/ˈfʌŋkʃn/ | دالة |
| side effect/saɪd ɪˈfekt/ | أثر جانبي |
| contract/ˈkɒntrækt/ | عقد |
| parameter/pəˈræmɪtə/ | معامل |
| argument/ˈɑːɡjuːmənt/ | حُجْجة |
| call frame/kɔːl freɪm/ | إطار استدعاء |
| scope/skəʊp/ | نطاق |
| local variable/ˈləʊkl ˈveərɪəbl/ | متغير محلي |
| decomposition/ˌdiːkɒmpəˈzɪʃn/ | تحليل |
امنح كل دالة مهمة واضحة واحدة
- الدالة تجمع مهمة مسماة. حدد مدخلاتها، وناتج الإرجاع وأي تأثير جانبي، مثل الطباعة، قبل كتابة الجسم.
- العقد يحدد ما قد يزود به المتصلون وما تعد به الدالة. هنا، المبالغ هي بنص كامل ويجب أن تكون أعدادًا صحيحة غير سالبة.
def add_fee(amount):
"""Return amount plus a fixed fee of 3 pence."""
return amount + 3
add_fee(8) تُقيّم لتصبح 11. لا تطبع شيئًا. فصل الحساب عن العرض يسمح لدالة أخرى بإعادة استخدام النتيجة.
أي عقد يسمح باستخدام المبلغ المتضمن فيه الرسوم مباشرة في عملية حسابية أخرى؟
الصعوبة 1/5 · 100 عملة + مكافأة صغيرة عند الإنجاز الأول اليوم
يمكن استخدام النتيجة المُرجعة في تعبير؛ بينما الطباعة تأثير جانبي منفصل.
فرّق بين اسم المدخل والكيان المدخل
- في التعريف،
amountهو معامل. فيadd_fee(8)، التعبير8يزود حجة. يتم تقييم تعابير الحجة قبل تشغيل جسم الدالة. - ربط الاستدعاء اسم المعامل بالكيان المزود. لا ينسخ هذا الكيان تلقائيًا ولا يخلق رابطًا بين اسم المتصل واسم المعامل.
amount = 4
result = add_fee(amount * 2)
print(amount, result)
يعطي تعبير الحجة قيمة 8؛ داخل الاستدعاء، يشير amount إلى هذا العدد الصحيح. تصبح القيمة المرتجعة 11 مساويةً لـ result. لا يزال amount الخاص بالمجلد يشير إلى 4، لذا يكون الناتج 4 11.
طابق كل مصطلح بدوره في add_fee(8).
الصعوبة 2/5 · 140 عملة + مكافأة صغيرة عند الإنجاز الأول اليوم
يتم تقييم الحُجَّة عند الاستدعاء؛ يتم ربط كائنها بالمعيار؛ يُعيد الدالة النتيجة.
احتفظ بإطار منفصل لكل استدعاء
- إطار الاستدعاء يسجل استدعاء دالة نشطًا واحدًا، بما في ذلك معلماته وأسماءه المحلية. عندما تستدعي دالة أخرى، ينتظر المتصل القيمة المرتجعة.
- اقرأ هذا البرنامج حسب ترتيب التنفيذ: المجلد →
twice→add_fee→twice→ المجلد. الأسهم أدناه تمثل تدفق التحكم المفاهيمي، وليست تخطيط ذاكرة فيزيائي.
def twice(amount):
subtotal = amount * 2
return add_fee(subtotal)
amount = 4
result = twice(amount)
print(amount, result)

استخدم التتبع لفحص الاستدعاءات والإرجاعات. تظهر تفاصيل الإطار الحالة عند الاستدعاء المتداخل. إنه تتبع مكتوب لهذا المثال تحديداً، وليس لمفسر بايثون.
تتبع استدعاء twice(4) للدالة add_fee(8)
فصل الارتباطات المحلية عن القيم المُرجعة.
رتّب هذه الأحداث حسب التسلسل الذي يتبعه result = twice(4).
الصعوبة 3/5 · 200 عملة + مكافأة صغيرة عند الإنجاز الأول اليوم
يكتمل الاستدعاء المتداخل قبل أن تتمكن twice من إكمال تعبير الإرجاع الخاص بها.
ما هو العدد الصحيح المربوط بـ amount داخل add_fee خلال هذا الاستدعاء المتداخل؟
الصعوبة 2/5 · 140 عملة + مكافأة صغيرة عند الإنجاز الأول اليوم
تحسب twice المجموع الجزئي بـ 4 × 2، ثم تُمرر ذلك 8 إلى add_fee.
أدرج نتيجة؛ اطبع رسالة
returnينهي الاستدعاء الحالي ويعيد كائنًا للمتصل. الدالة التي تصل إلى نهايتها بدونreturnتُرجعNone.printيكتب المخرجات. كما أنه يُعيدNone، لذا فإن طباعة نتيجة حساب داخل دالة لا تجعل تلك النتيجة هي ما تُعاد.
def show_fee(amount):
print(amount + 3)
value = show_fee(8)
print(value)
هذا يطبع 11، ثم None، على أسطر منفصلة. إذا كان المُدعِي يحتاج إلى الرقم، استخدم return amount + 3 واترك للمُدعِي تحديد ما إذا كان سيطبعه أم لا.
يطبع show_fee 11 ولكن ليس لديه جملة إرجاع. ما الذي يُخزن في value؟
الصعوبة 1/5 · 100 عملة + مكافأة صغيرة عند الإنجاز الأول اليوم
الخروج من نهاية دالة بايثون يُرجع None، بغض النظر عن مخرجات الطباعة.
حل الأسماء في النطاق الصحيح
- المتغير المحلي ينتمي إلى استدعاء الدالة. يحدد النطاق أين يمكن حل الاسم؛ لا تحصل دالة أخرى تلقائيًا على الوصول إلى المتغيرات المحلية للدالة المُدعية.
- تعيين قيمة جديدة للعدد صحيح في المعامل يغير ارتباطه المحلي. ولا يعيد تعيين متغير بنفس الاسم في الدالة المُدعية.
def increase(amount):
amount = amount + 1
return amount
amount = 4
new_amount = increase(amount)
print(amount, new_amount)
المخرجات هي 4 5. كلا الاسمين يشيران في البداية إلى العدد الصحيح 4؛ إعادة الارتباط داخل الدالة لتغيير ارتباط اسمه المحلي. لتحديث اسم الوحدة، اكتب amount = increase(amount).
بعد new_amount = increase(amount)، ما القيمة التي لا تزال يحتفظ بها amount في الوحدة؟
الصعوبة 2/5 · 140 عملة + مكافأة صغيرة عند الإنجاز الأول اليوم
تم إعادة ربط الاسم المحلي للدالة فقط؛ لم يقم المُنشئ بتعيين قيمة لـ amount.
تفكيك الحساب إلى أجزاء قابلة للاختبار
- التفكيك يفصل مهمة أكبر إلى مهام أصغر بعقود واضحة. بالنسبة لطلب، احسب المجموع الفرعي للبند، أضف رسومًا، ثم اعرض المبلغ النهائي.
- اختبر الحسابات مباشرة، بما في ذلك الكمية صفر وأكثر من بند واحد. نصّ على أن المدخلات أعداد صحيحة غير سالبة؛ يمكن أن تكون التحقق من صحة المدخلات دالة منفصلة.
def item_subtotal(unit_pence, quantity):
return unit_pence * quantity
def order_total(unit_pence, quantity):
return add_fee(item_subtotal(unit_pence, quantity))
order_total(5, 2) يعطي 13؛ order_total(5, 0) يعطي 3 تحت عقد الرسوم الثابتة هذا. إذا كان يجب أن تكلف الطلبات الخالية من البند صفرًا، غيّر العقد والتنفيذ معًا.
بناءً على عقد الرسوم الثابت المذكور، ماذا يُرجع order_total(5, 0) بالبنسات؟
الصعوبة 2/5 · 140 عملة + مكافأة صغيرة عند الإنجاز الأول اليوم
المجموع الجزئي للسلعة صفر، لكن add_fee يضيف 3 nonetheless. تتطلب الطلبات الفارغة المجانية عقداً مختلفاً.
شرح الاستدعاء الكامل قبل الوثوق بالإجابة
- تتبع تعابير الحجج، وروابط المعاملات، والحسابات المحلية، والقيم المُعادة. تحقق من حالة المُدعِي بعد الاستدعاء والمخرجات بشكل منفصل.
- لا تصف تمرير المعاملات بنسخ كل كائن. المجموعة القابلة للتعديل يمكن تغييرها عبر المعامل؛ تتبع مخططات الكائنات للطعن والتشابه في الوحدة 1.5.
لا يثبت رقم مطبوع صحيح أن النتيجة المُعادة صحيحة. استخدم فحوصات مباشرة مثل assert order_total(5, 2) == 13، ثم افحص أي مخرجات متوقعة بشكل منفصل.
مراجع اللغة: دوال بايثون وربط الأسماء والنطاق.
استدعاء دالة بايثون ينسخ تلقائياً كل كائن حُجْجي.
الصعوبة 1/5 · 100 عملة + مكافأة صغيرة عند الإنجاز الأول اليوم
تُربط المعايير بالكائنات المزوّدة. يجب طلب النسخ صراحة عند الحاجة.