الدوال في بايثون
الداله في بايثون : تعني Methods او Functions في اللغة الإنجليزية, و هي عبارة عن مجموعة أوامر مجمعة في مكان واحد و تتنفذ عندما نقوم باستدعائها .
كما أن بايثون تحتوي على مجموعة كبيرة جداً من الدوال الجاهزة التي سبق أن إستخدامنا بعضها مثل الدوال print()
و min()
و max()
و غيرهم من الدوال التي تطرقنا إليها في دروس سابقة .
الدرس التالي :التعامل مع ال modules في بايثون .
الدرس السابق :الكلاس dict في بايثون .
مصطلحات في الدوال
الدوال الجاهزة في بايثون, يقال لها Built-in Functions.
الدوال التي يقوم المبرمج بتعريفها, يقال لها User-defined Functions.
طريقة تعريف دوال جديدة في بايثون
الشكل الأساسي الذي يجب إتباعه عند تعريف أي دالة في بايثون هو التالي:
function_suite
• def: تعني أنك تعرف دالة جديدة.
• functionname: نضع مكانها الإسم الذي نعطيه للدالة, و الذي من خلاله يمكننا استدعاءها.
• (): بداخل القوسين يمكنك وضع باراميترات و يجب أن تضع :
مباشرةً بعد القوسين و من ثم تنزل على سطر جديد لتبدأ بكتابة الأوامر التي ستتنفذ عند إستدعاء الدالة.
• function_suite: تعني الأوامر التي سنضعها في الدالة و التي ستتنفذ عند إستدعاءها.
إنتبه
أنت مجبر على وضع 4 مسافات فارغة قبل الأوامر التي ستضعها في الدالة حتى يعرف مفسّر لغة بايثون أن هذه الاوامر موجودة بداخل الدالة.
للترتيب و لكتابة الكود كما يفعل باقي المبرمجين, قم بإضافة سطرين فارغين بعد تعريف الدالة.
في المثال التالي قمنا بتعريف دالة إسمها my_function
وضعنا فيها أمر طباعة واحد فقط. بعدها قمنا باستدعائها.
المثال الأول
# my_function هنا قمنا بتعريف دالة إسمها def my_function(): print('My first function is called') # حتى يتنفذ الأمر الموضوع فيها my_function هنا قمنا باستدعاء الدالة my_function()
•سنحصل على النتيجة التالية عند التشغيل.
جميع المعلومات التي يمكن ذكرها عند تعريف دالة جديدة
قبل قليل تكلمنا عن الأشياء الأساسية التي يجب أن تكون متوفرة عند تعريف أي دالة.
الآن, عليك معرفة أنه يمكنك وضع مزيد من التفاصيل بشرط أن تضيفها ضمن ترتيب محدد و ليس شرطاً أن تضيفها كلها.
""" function_docstring """
function_suite
return [expression]
• def: تعني أنك تعرف دالة جديدة.
• functionname: نضع مكانها الإسم الذي نعطيه للدالة, و الذي من خلاله يمكننا استدعاءها.
• parameters: المقصود بها الباراميترات التي نمررها لها عند إستدعاءها ( وضع الباراميترات هو أمر إختياري ).
• """ function_docstring """: نضع مكانها نص الهدف منه تفسير ما تفعله الدالة بشكل مختصر ( وضع التفسير هو أمر إختياري ).
• function_suite: تعني الأوامر التي نضعها في الدالة.
• return [expression]: نضع مكانها ما يمكن أن ترجعه الدالة في المكان الذي تم إستدعاءها منه ( إرجاع قيمة هو أمر إختياري ).
هنا قمنا بتعريف دالة function إسمها greeting
, عند إستدعاءها نمرر لها إسم فتطبع رسالة ترحيب للإسم الذي تم تمريره لها.
المثال الثاني
# تحتوي على باراميتر واحد. عند إستدعاءها نمرر لها إسم, فتطبع جملة ترحيب بإسم الشخص الذي نمرره لها greeting هنا قمنا بتعريف دالة إسمها def greeting(name): """ This function print hello message based on the specified name """ print('Hello '+name+', welcome to our company.') # user هنا قمنا بتخزين إسم الشخص الذي سنمرره للدالة في المتغير user = 'Ahmad' # حتى تطبع رسالة ترحيب له user و تمرير إسم الشخص الذي قمنا بتخزينه في المتغير greeting() هنا قمنا باستدعاء الدالة greeting(user)
•سنحصل على النتيجة التالية عند التشغيل.
هنا قمنا بكتابة نفس الدالة السابقة لكننا قمنا هذه المرة بطباعة الشرح الموضوع في الدالة ( أي الـ doc string ) و ليس إستدعاءها.
المثال الثالث
# تحتوي على باراميتر واحد. عند إستدعاءها نمرر لها إسم, فتطبع جملة ترحيب بإسم الشخص الذي نمرره لها greeting هنا قمنا بتعريف دالة إسمها def greeting(name): """ This function print hello message based on the specified name """ print('Hello '+name+', welcome to our company.') # greeting() هنا قمنا بطباعة الشرح المرفق بالدالة print(greeting.__doc__)
•سنحصل على النتيجة التالية عند التشغيل.
في المثال التالي قمنا بتعريف دالة إسمها get_sum
, عند إستدعاءها نمرر لها عددين فترجع لنا ناتج جمعهما.
المثال الرابع
# عند إستدعاءها نمرر لها عددين فتقوم بإرجاع ناتج جمعهما get_sum هنا قمنا بتعريف دالة إسمها def get_sum(a, b): """This method returns the sum of the numbers that you passed in a and b""" return a + b; # x في المتغير get_sum() هنا قمنا بتخزين ناتج العددين 3 و 5 الذي سترجعه الدالة x = get_sum(3, 5) # و التي ستساوي 8 x هنا قمنا بعرض قيمة المتغير print(x)
•سنحصل على النتيجة التالية عند التشغيل.
وضع قيم إفتراضية للباراميترات في الدوال
بايثون تتيح لك وضع قيم إفتراضية للباراميترات مما يجعلك عند إستدعاء الدالة مخيّر على تمرير قيم مكان الباراميترات بدل أن تكون مجبراً على ذلك.
مصطلحات تقنية
القيمة الإفتراضية التي نضعها للباراميتر يقال لها Default Argument.
Technical termsفي المثال التالي قمنا بتعريف دالة إسمها print_language
.
هذه الدالة فيها باراميتر واحد إسمه language
يملك النص 'English'
كقيمة إفتراضية.
كل ما تفعله هذه الدالة عند إستدعاءها هو طباعة قيمة الباراميتر language
.
ملاحظة: بما أن الباراميتر language
يملك قيمة بشكل إفتراضية, فهذا يعني أنك لم تعد مجبر على تمرير قيمة له عند إستدعاء الدالة لأنه أصلاً يملك قيمة.
مثال
# و يمكنك عدم تمرير قيمة لأنه أصلاً يملك قيمة language عند إستدعاءها يمكنك تمرير قيمة لها مكان الباراميتر .print_language هنا قمنا بتعريف دالة إسمها def print_language(language='English'): print('Your language is:', language) # 'English' و بالتالي ستظل قيمته language بدون تمرير قيمة مكان الباراميتر print_language() هنا قمنا باستدعاء الدالة print_language() # 'Arabic' و بالتالي ستصبح قيمته language للباراميتر 'Arabic' مع تمرير القيمة print_language() هنا قمنا باستدعاء الدالة print_language('Arabic')
•سنحصل على النتيجة التالية عند التشغيل.
Your language is: Arabic
تحديد أسماء الباراميترات التي سيتم إعطاءها قيم في دوال بايثون
في أغلب لغات البرمجة عندما تقوم بإستدعاء دالة تحتوي على عدة باراميترات, تكون مجبر على تمرير قيم لهذه الباراميترات بنفس الترتيب الذي تم وضعهم فيه.
في لغة بايثون عندما تقوم باستدعاء دالة تحتوي على عدة باراميترات, يمكنك إعطاء قيم لهذا الباراميترات بدون الحاجة إلى التقيد بنفس الترتيب الذي تم وضعهم فيه حيث أنك تستطيع بداخل أقواس الدالة ذكر إسم الباراميتر و إسناد قيمة إليه و عندها لا يهم إن اتبعت الترتيب الموضوع أم لا.
في المثال التالي قمنا بتعريف دالة إسمها print_info
تحتوي على باراميترين name
و salary
.
بعدها قمنا باستدعائها مرتين:
- في المرة الأولى, قمنا بتمرير قيم للباراميترات بالترتيب الذي تم وضعهم فيه.
- في المرة الثاني قمنا بتمرير نفس القيم للباراميترات و لكن بدون التقيد بالترتيب الموضوعين فيه.
مثال
# salary و قيمة مكان الباراميتر name عند إستدعاءها يجب أن نمرر لها قيمة مكان الباراميتر print_info هنا قمنا بتعريف دالة إسمها def print_info(name, salary): print('Name:', name) print('Salary:', salary) print('------------------') # بنفس الترتيب الموضوعين فيه salary و الرقم 1500 للباراميتر name للباراميتر 'Nader' مع تمرير النص print_info هنا قمنا بإستدعاء الدالة print_info('Nader', 1500) # name سيتم وضعه في الباراميتر 'Nader' و النص salary مع تحديد أن الرقم 1500 سيتم وضعه في الباراميتر print_info هنا قمنا بإستدعاء الدالة print_info(salary=1500, name='Nader')
•سنحصل على النتيجة التالية عند التشغيل.
Salary: 1500
------------------
Name: Nader
Salary: 1500
------------------
بناء دوال تقبل عدد غير محدد من القيم عند إستدعاء الدوال في بايثون
في بعض الأحيان قد تحتاج إلى بناء دالة تعالج عدد غير محدد من القيم عند استدعاءها. أي مهما كان عدد القيم التي ستمررها لها فإنها يجب أن تعالجهم كلهم.
لبناء دالة يمكن تمرير عدد غير محدد من القيم لها عند إستدعاءها عليك تجهيز باراميتر واحد لهذا الأمر.
ببساطة ضع *
قبل إسم الباراميتر و عندها سيفهم مفسّر لغة بايثون أنه يمكنك تمرير عدد غير محدد من القيم لهذا الباراميتر. عندها سيتم تجميع كل القيم التي تمررها للدالة بداخل tuple
مما يجعلك قادراً على التعامل معهم بكل سهولة.
مصطلحات تقنية
في حال كان عدد القيم التي يمكن تمريرها للباراميتر غير محدد يقال لهذه القيم Variable-length Arguments.
في المثال التالي قمنا بتعريف دالة إسمها print_args
تقبل عدد غير محدد من القيم عند إستدعاءها بعدها تقوم فقط بعرض هذه القيم بواسطة الحلقة for
.
جميع القيم التي سيتم تمريرها لها, سيتم تخزينها في باراميتر واحد إسمه *args
.
المثال الأول
# عند إستدعاءها يمكننا تمرير عدد غير محدد من القيم لها. بعدها ستقوم بطباعة القيم التي مررناها لها print_args هنا قمنا بتعريف دالة إسمها def print_args(*args): for e in args: print(e) # مع تمرير 10 قيم لها print_args() هنا قمنا بإستدعاء الدالة print_args(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
•سنحصل على النتيجة التالية عند التشغيل.
2
3
4
5
6
7
8
9
10
في المثال التالي قمنا بتعريف دالة إسمها print_average
تقبل عدد غير محدد من القيم عند إستدعاءها.
الهدف من هذه الدالة طباعة متوسط القيم التي تم تمريرها إليها و الذي يساوي ناتج جمع القيم التي تمريرها مقسوماً على عددهم.
جميع القيم التي سيتم تمريرها لها, سيتم تخزينها في باراميتر واحد إسمه *values
.
ملاحظة: قمنا بالإعتماد على الدالتين sum()
و len()
الجاهزتين في بايثون لنحصل على ناتج جمع القيم التي تم تمريرها و عددهم.
المثال الثاني
# عند إستدعاءها يمكننا تمرير عدد غير محدد من القيم لها. بعدها ستقوم بطباعة متوسط القيم التي مررناها لها print_average هنا قمنا بتعريف دالة إسمها def print_average(*values): print(sum(values)/len(values)) # مع تمرير 4 قيم لها print_total() هنا قمنا بإستدعاء الدالة print_average(1, 2, 3, 4)
•سنحصل على النتيجة التالية عند التشغيل.
في المثال التالي قمنا بتعريف دالة إسمها print_user_average
, الهدف منها طباعة إسم الشخص و متوسط العلامات التي نالها.
إذاً, عند إستدعاءها يجب أن نمرر لها قيمتين على الأقل:
- القيمة الأولى تمثل إسم شخص و الذي سنقوم بتخزينه في باراميتر إسمه user
.
- القيمة الثانية أو مجموعة القيم الثانية تمثل علامات هذا الشخص و التي سنقوم بتخزينها في باراميتر واحد إسمه *notes
.
ملاحظة: قمنا بالإعتماد على الدالتين sum()
و len()
الجاهزتين في بايثون لنحصل على ناتج جمع القيم التي تم تمريرها و عددهم.
المثال الثالث
# notes و علاماته مكان الباراميتر user عند إستدعاءها يجب أن نمرر لها إسم الشخص مكان الباراميتر .print_user_average هنا قمنا بتعريف دالة إسمها # و عندها ستقوم بحساب معدل الشخص ( بناءاً على علاماته ) و من ثم عرض إسمه و معدله بشكل مرتب def print_user_average(user, *notes): avg = sum(notes)/len(notes) print('The average of', user, 'is:', avg) # مع تمرير إسم الشخص و 4 قيم ( و التي تمثل علامات الشخص ) لها print_user_average() هنا قمنا بإستدعاء الدالة print_user_average('Ahmad', 1, 2, 3, 4)
•سنحصل على النتيجة التالية عند التشغيل.
التفريق بين المتغيرات المعرفة بداخل الدوال و خارجها في بايثون
عند تعريف دوال جديدة عليك الإنتباه لأسماء المتغيرات التي تنوي تعريفها بداخلها حتى لا يحدث تضارب بينها و بين باقي المتغيرات الموجودة أصلاً في الكود.
فمثلاً, في حال قمت بتعريف متغير عادي, ثم قمت بتعريف دالة وضعت فيها متغير يحمل نفس إسم المتغير الموجود خارج الدالة. عندها سيتجاهل مفسّر لغة بايثون المتغير الذي تم تعريفه خارج الدالة و بالتالي لن تستطيع الوصول إليه من الدالة, أي لن تستطيع حتى أن تعرض قيمته.
في حال كنت تريد إعلام مفسّر لغة بايثون أنك تريد التعامل مع المتغير الموجود خارج الدالة سواء من أجل عرض قيمته أو تغييرها سيكون عليك وضع الكلمة المفتاحية global
قبل إسم المتغير و على سطر منفرد.
مصطلحات تقنية
المتغيرات التي يتم تعريفها بداخل الدوال, يقال لها Local Variables و هذه التسمية تعني أنه لا يمكن الوصول لها من خارج الدالة بشكل مباشر.
المتغيرات التي يتم تعريفها خارج الدوال, يقال لها Global Variables و هذه التسمية تعني أنه يمكن الوصول لها من أي مكان في الكود حتى من داخل الدوال.
في المثال التالي قمنا بتعريف متغير إسمه x
, ثم قمنا بتعريف دالة إسمها test
تقوم فقط بطباعة قيمته.
المثال الأول
# قيمته تساوي 1 x هنا قمنا بتعريف متغير إسمه x = 1 # الذي تم تعريفه خارجها x تطبع قيمة المتغير test هنا قمنا بتعريف دالة إسمها def test(): print('Global x =', x) # الذي تم تعريفه خارجها x و التي ستطبع قيمة المتغير test() هنا قمنا باستدعاء الدالة test()
•سنحصل على النتيجة التالية عند التشغيل.
في المثال التالي قمنا بتعريف متغير إسمه x
, ثم قمنا بتعريف دالة إسمها test
تحتوي أيضاً على متغير إسمه x
تقوم فقط بطباعة قيمته.
تذكر: سيتم طباعة قيمة x
الذي تم تعريفه بداخل الدالة و ليس الموجود خارجها لأن مفسّر لغة بايثون سيتجاهل المتغير الخارجي.
المثال الثاني
# قيمته تساوي 1 x هنا قمنا بتعريف متغير إسمه x = 1 # الذي تم تعريفه بداخلها x تطبع قيمة المتغير test هنا قمنا بتعريف دالة إسمها def test(): x = 5 print('Local x =', x) # الذي تم تعريفه بداخلها x و التي ستطبع قيمة المتغير test() هنا قمنا باستدعاء الدالة test() # الموجود خارج الدالة. لاحظ أن قيمته لم تتغير x هنا قمنا بطباعة قيمة المتغير print('Global x =', x)
•سنحصل على النتيجة التالية عند التشغيل.
Global x = 1
في المثال التالي قمنا بتعريف متغير إسمه x
قيمته 1, ثم قمنا بتعريف دالة إسمها test
تقوم فقط بتغيير قيمته إلى 5.
تذكر: لتغيير قيمة متغير من داخل دالة لكنه في الأساس موجود خارجها يجب وضع الكلمة المفتاحية global
قبل إسم المتغير و على سطر منفرد.
المثال الثالث
# قيمته تساوي 1 x هنا قمنا بتعريف متغير إسمه x = 1 # الذي تم تعريفه خارجها x تقوم بتغير قيمة test هنا قمنا بتعريف دالة إسمها def test(): global x x = 5 # global الذي قمنا بتعريفه في الأساس خارجها و الذي وصلنا إليه بواسطة الكلمة x حتى تقوم بتغيير قيمة المتغير test() هنا قمنا باستدعاء الدالة test() # الموجودة في خارج الدالة. لاحظ أنها بقيت كما هي x هنا قمنا بطباعة قيمة print('Global x =', x)
•سنحصل على النتيجة التالية عند التشغيل.
تعريف الدوال في بايثون بأسلوب Lambda
في حال كنت تريد تعريف دالة تتألف من سطر واحد فقط و ترجع قيمة عند إستدعاءها, يمكنك تعريفها كـ Anonymous Function. و عندها تستخدم الكلمة المفتاحية lambda
لتعريفها و ليس الكلمة def
.
الشكل الأساسي الذي يجب إتباعه عند تعريف دالة بأسلوب Lambda في بايثون هو التالي:
• lambda: تعني أنك تعرف دالة جديدة ليس لها إسم.
• [arg1 [,arg2,.....argn]]: يقصد منها فقط الباراميترات التي يمكنك وضعها للدالة مع الإشارة إلى أنه يجب وضع فاصلة بين كل باراميترين.
• :expression: تعني الأمر الذي سيرجع القيمة عندما تتنفذ الدالة مع الإشارة إلى أنه يجب وضع الرمز :
هو أمر إجباري.
ملاحظة
عندما تقوم بتعريف Anonymous Function فأنت فعلياً تقوم بتعريف دالة ليس لها إسم. و بما أنه لا يمكن إستدعاء دالة إلا من خلال مناداتها باسمها, فالحل هو أن تشير للدالة بإسم متغير. أي تقوم بتعريف متغير و تجعله يساوي الدالة التي قمت بتعريفها و عندها سيتم إعتبار إسم المتغير هو إسم الدالة الذي من خلاله يمكن مناداتها.
في المثال التالي قمنا بتعريف دالة كـ Anonymous Function عند استدعاءها نمرر لها عددين فترجع ناتج طرح العدد الأول من العدد الثاني.
قمنا بإسناد الدالة إلى كائن إسمه total_pay
حتى نستطيع مناداته من خلالها.
مثال
# total_pay هنا قمنا بتعريف دالة يجب أن نمرر لها قيمتين عند إستدعاءها حتى ترجع ناتج طرحهما و يمكن مناداتها بواسطة الكائن total_pay = lambda price, tax: price - tax # و كل ما ستفعله هو إرجاع ناتج طرح القيمة الاولى من القيمة الثانية total_pay هنا قمنا بإستدعاء الدالة بواسطة الكائن print(total_pay(500, 40))
•سنحصل على النتيجة التالية عند التشغيل.