Java Backend Software Engineer
آموزش پایتون - فصل سوم(توابع)
در زمینه ی برنامه نویسی ، تابع دنباله ی نامگذاری شده ای از دستورات هست که محاسباتی رو برای ما انجام میده . از طریق مشخص کردن نام و دنباله دستورات ، یک تابع رو تعریف میکنیم و بعد میتونیم با استفاده از نام تابع ، اون رو فراخوانی بکنیم .
فراخوانی تابع :
قبلا نمونه ای از فراخوانی تابع رو باهم دیدیم :
>>> type(42)
<class 'int'>
نام تابع ، type هست و عبارت داخل پرانتز ، آرگومان تابع نامیده میشه . تابع بالا ، نوع آرگومانی که بهش پاس دادیم رو در خروجی چاپ میکنه .
در واقع میتونیم بگیم که یک تابع ، آرگومان رو میگیره و نتیجه رو به ما برمیگردونه ؛ به نتیجه ی تابع ، مقدار برگشتی هم گفته میشه .
پایتون توابعی رو برای تبدیل انواع داده ای در اختیار ما قرار داده . برای مثال تابع int مقداری رو به عنوان آرگومان میگیره و اگر بتونه اون رو به عدد صحیح تبدیل میکنه ؛ اگر هم نتونه به ما اخطار میده :
>>> int('32')
32
>>> int('Hello')
ValueError: invalid literal for int(): Hello
تابع int میتونه مقادیر اعشاری رو به مقادیر صحیح تبدیل کنه ولی نکته ای که وجود داره اینه که بجای گرد کردن مقادیر اعشاری ، فقط قسمت اعشاری اونها رو حذف میکنه :
>>> int(3.99999)
3
>>> int(-2.3)
-2
تابع float هم مقادیر صحیح و رشته ای رو به عدد اعشاری تبدیل میکنه :
>>> float(32)
32.0
>>> float('3.14159')
3.14159
و در نهایت تابع str آرگومانی که بهش پاس دادیم رو به مقدار رشته ای تبدیل میکنه :
>>> str(32)
'32'
>>> str(3.14159)
'3.14159'
توابع ریاضی :
در پایتون ماژولی بنام math وجود داره که اکثر توابع معروف ریاضی رو در اختیار ما قرار میده . ماژول به فایلی گفته میشه که شامل مجموعه ای از توابع مرتبط هست.
قبل از اینکه بتونیم از توابع داخل یک ماژول استفاده کنیم ، باید ماژول مورد نظر رو با دستور import به پروژه خودمون اضافه کنیم :
import math
این دستور باعث ایجاد یک شیِ ماژول به نام math میشه . شیِ ماژول شامل توابع و متغیر های تعریف شده در داخل یک ماژول هست . برای دسترسی به هر یک از این توابع ، باید نام ماژول به همراه نام تابع که با یک نقطه از هم جدا شده اند رو وارد کنیم ؛ به این روش dot notation گفته میشه :
>>> ratio = signal_power / noise_power
>>> decibels = 10 * math.log10(ratio)
>>> radians = 0.7
>>> height = math.sin(radians)
در مثال اول از math.log10 برای محاسبه ی نسبت سیگنال به نویز در واحد دسیبل استفاده میکنیم (فرض کنید متغیرهای signal_power و noise_power از قبل تعریف شدن). علاوه بر این ، ماژول math تابع log رو برای محاسبه ی لگاریتم در مبنای e در اختیار ما قرار داده.
مثال دوم مقدار سینوس متغیر radians رو برای ما محاسبه میکنه . نکته ای که اینجا باید بهش اشاره کنم اینه که تابع سینوس و دیگر توابع مثلثاتی مانند کسینوس ، تانژانت و .... آرگومان رو بجای درجه بصورت رادیان دریافت میکنند.
برای تبدیل درجه به رادیان ، درجه رو بر 180 تقسیم کرده و در pi ضرب میکنیم :
>>> degrees = 45
>>> radians = degrees / 180.0 * math.pi
>>> math.sin(radians)
0.707106781187
عبارت math.pi از داخل ماژول math ، مقدار تقریبی عدد پی که تا 15 رقم اعشار محاسبه شده رو در اختیا ما قرار میده .
ترکیب بندی (composition):
تا اینجای کار با المان های سازنده ی برنامه ها مانند متغیرها ، عبارات و دستورات کار کردیم ولی همه ی این مفاهیم رو بصورت مجزا استفاده کردیم بدون اینکه در مورد ترکیب این المان ها حرفی زده باشیم. یکی از ویژگی های زبان های برنامه نویسی ، توانایی ایجاد بلوک های کوچک کد و ترکیب اونها هست. برای مثال آرگومان یک تابع میتونه شامل هر نوع عبارتی باشه مثلا عملگرهای محاسباتی :
x = math.sin(degrees / 360.0 * 2 * math.pi)
و یا شامل فراخوانی یک تابع باشه :
x = math.exp(math.log(x+1))
نکته : شما تقریبا در هر جایی میتونید مقدار و یا عبارت دلخواهی رو قرار بدید و در این مورد فقط یک استثنا وجود داره ؛ در سمت چپِ هر عبارتِ انتساب باید نام یک متغیر قرار بگیره و قرار دادن هر نوع عبارت دیگه ای باعث ایجاد خطای نحوی میشه :
>>> minutes = hours * 60 # right
>>> hours * 60 = minutes # wrong
SuntaxError : can't assign to operator
افزودن توابع جدید :
تا اینجای کار ما داشتیم از توابع موجود در داخل زبان پایتون استفاده میکردیم و الان میخوایم که توابع جدید خودمون رو ایجاد بکنیم. تعریف یک تابع با نام تابع و دنباله ی دستوراتی که هنگام فراخوانی تابع اجرا میشن ، مشخص میشه . برای مثال :
def print_lyrics():
print("I'm Milad, and I'm okay.")
print("I sleep all night and I work all day.")
کلمه ی def نشون میده که این عبارت مربوط به تعریف یک تابع هست و همچنین عبارت print_lyrics به عنوان نام تابع در نظر گرفته میشه .
نکته : قوانین نامگذاری توابع دقیقا مانند قوانین نامگذاری متغیر ها در پایتون هست. ما نمیتونیم از کلید واژه های پایتون برای نامگذاری توابعمون استفاده کنیم ؛ در ضمن از انتخاب یک نام مشترک برای یک تابع و یک متغیر باید خودداری کنیم.
پرانتز باز و بسته نشون دهنده ی این هست که این تابع هیچ آرگومانی رو دریافت نمیکنه . به اولین خط تعریف تابع ، هِدِر گفته میشه و مابقی به عنوان بدنه ی تابع شناخته میشن . در آخرِ هدر تابع باید دونقطه (:) قرار داده بشه و بدنه ی تابع نیز بصورت پیشفرض به اندازه ی ۴ عدد space تورفتگی خواهد داشت تا ساختار تابع به درستی شکل بگیره .
در صورتی که مستقیما با مفسر پایتون کار میکنید ، هنگام تعریف یک تابع ، مفسر نقطه هایی رو بری شما نمایش خواهد داد تا بهتون اطلاع بده که فرایند تعریف تابع هنوز کامل نشده :
>>> def print_lyrics():
... print("I'm Milad, and I'm okay.")
... print("I sleep all night and I work all day.")
...
برای پایان دادن به بدنه ی تابع ، بعد از سه نقطه نباید دستوری وارد کنیم .
هنگام تعریف یک تابع ، یک شئِ تابع ساخته میشه که از نوع تابع هست :
>>> print(print_lyrics)
<function print_lyrics at 0xb7e99e9c>
>>> type(print_lyrics)
<class 'function'>
برای فراخوانی توابعی که ساختیم ، مانند فراخوانی توابع داخل پایتون عمل میکنیم :
>>> print_lyrics()
I'm Milad, and I'm okay.
I sleep all night and I work all day.
بعد از تعریف یک تابع ، میتونیم اون رو داخل توابع دیگه فراخوانی کنیم . برای مثال تابعی به نام repeat_lyrics میسازیم و تابع قبلی رو داخل اون دوبار فراخوانی میکنیم :
def repeat_lyrics():
print_lyrics()
print_lyrics()
و بعد تابع repeat_lyrics رو فراخوانی میکنیم :
>>> repeat_lyrics()
I'm Milad, and I'm okay.
I sleep all night and I work all day.
I'm Milad, and I'm okay.
I sleep all night and I work all day.
تعاریف و کاربردها :
در این بخش میخوایم که توابعی که ساخته بودیم رو داخل یک برنامه و کنار هم بنویسیم و تغییراتی رو روی اونها انجام بدیم :
def print_lyrics():
print("I'm Milad, and I'm okay.")
print("I sleep all night and I work all day.")
def repeat_lyrics():
print_lyrics()
print_lyrics()
repeat_lyrics()
خب ، این برنامه شامل دو تعریف تابع هست . تعاریف توابع مانند بقیه دستورات اجرا میشن ولی بجز ساختن شئ تابع ، هیچ کار دیگه ای انجام نمیدن. دستورات داخل توابع تا زمانی که تابع مورد نظر فراخوانی نشده ، اجرا نمیشن و طبیعتا هیچ خروجی ای هم تولید نمیشه .
نکته مهم : برای فراخوانی یک تابع باید اون تابع قبلا تعریف شده باشه .
به عنوان مثال و برای اینکه بیشتر متوجه بشید ، در برنامه ی بالا ، جای آخرین خط برنامه (فراخوانی تابع) رو عوض کنید و اون رو به اول برنامه انتقال بدید . ببینید که بعد از اجرای برنامه با چه خطایی مواجه میشید.
حالا برنامه رو به حالت اول برگردونید و این بار تابع print_lyrics رو بعد از تابع repeat_lyrics تعریف کنید . بررسی کنید که بعد از اجرای این کار چه اتفاقی خواهد افتاد.
روند اجرا :
روند اجرای یک برنامه همیشه از اولین دستور اون شروع میشه ؛ دستورات موجود در برنامه یک بار از بالا به پایین اجرا میشن .
تعریف توابع روند اجرای برنامه رو تغییر نمیدن و همونطور که قبلا هم گفتیم دستورات داخل توابع تا زمانی که اونها رو فراخوانی نکرده باشیم اجرا نخواهند شد. فراخوانی یک تابع مانند راه انحرافی در روند اجرای برنامه هست ؛ به این صورت که بجای رفتن به دستور بعدی ، روند اجرا به داخل بدنه ی تابع انتقال پیدا میکنه و دستورات داخل تابع اجرا میشن و بعد از پایان کار ، روند اجرا دوباره بجای قبلی خودش برمیگرده و دستور بعدی اجرا میشه . خیلی ساده :)
نکته ای که باید به یاد داشته باشید اینه که یک تابع میتونه تابع دیگری رو فراخوانی بکنه . مثلا در میانه ی بدنه ی یک تابع ، برنامه مجبور میشه که دستورات موجود در یک تابع دیگری رو اجرا بکنه و همینطور مادامی که در حال اجرای دستورات تابع جدید هست ، مجبور باشه که دستورات یک تابع دیگری رو هم اجرا بکنه.
بطور خلاصه وقتی قصد داریم که کدهای یک برنامه رو بخونیم و بررسی کنیم بهتره بجای خوندن از بالا به پایین ، روند اجرای برنامه رو طی کنیم تا حس تشخیص و درک بیشتری داشته باشیم .
پارامترها و آرگومان ها :
بعضی از توابعی که قبلا باهم دیدم آرگومان هایی رو نیاز داشتند . برای مثال وقتی تابع math.sin رو فراخوانی میکنیم ، عددی رو به عنوان آرگومان به اون پاس میدیم. بعضی از توابع بیش از یک آرگومان میگیرن مانند تابع math.pow که دو آرگومان رو میگیره ؛ اولی به عنوان پایه و دومی به عنوان توان .
در داخل توابع ،مقدار آرگومان ها به متغیر هایی بنام پارامتر ها انتقال پیدا میکنن . برای مثال در اینجا تابعی رو تعریف کردیم که یک آرگومان رو دریافت میکنه :
def print_twice(name):
print(name)
print(name)
این تابع مقدار آرگومان دریافتی رو به پارامتری بنام name انتقال میده و هنگام فراخوانی تابع ، مقدار پارامتر (هرچیزی که باشه) دوبار چاپ خواهد شد.
این تابع با هر مقداری که قابل چاپ شدن باشه ، میتونه کار کنه :
>>> print_twice('Spam')
Spam
Spam
>>> print_twice(42)
42
42
>>> print_twice(math.pi)
3.14159265359
3.14159265359
مشابه قوانین ترکیب بندی برای توابع درون-ساخت(Built-in) زبان پایتون ، این قوانین ، توابع ساخته شده توسط کاربر رو هم شامل میشه و میتونید از هر نوع عبارتی به عنوان آرگومان برای تابع print_twice استفاده کنید :
>>> print_twice('Spam '*4)
Spam Spam Spam Spam
Spam Spam Spam Spam
>>> print_twice(math.cos(math.pi))
-1.0
-1.0
همچنین ما میتونیم از یک متغیر به عنوان آرگومان تابع استفاده کنیم :
>>> var1 = "Python Is Amazing"
>>> print_twice(var1)
Python Is Amazing
Python Is Amazing
متغیرها و پارامترهای محلی :
زمانی که شما متغیری رو داخل یک تابع ایجاد میکنید ، اون متغیر یک متغیر محلی هست ؛ به این معنی که فقط داخل اون تابع وجود داره . برای مثال :
def cat_twice(part1, part2):
cat = part1 + part2
print_twice(cat)
این تابع دو آرگومان رو از ما دریافت میکنه ، اونها رو بهم میچسبونه و نتیجه رو دوبار برای ما چاپ میکنه. از تابع بالا بصورت زیر میتونیم استفاده کنیم :
>>> line1 = ' this is the first string '
>>> line2 = ' and that is the second string'
>>> cat_twice(line1, line2)
'this is the first string and that is the second string'
'this is the first string and that is the second string'
بعد از اینکه تابع cat_twice خاتمه پیدا کرد ، متغیر cat از بین میره و اگر بخوایم که مقدار اون رو چاپ کنیم با خطای زیر مواجه میشیم :
>>> print(cat)
NameError: name 'cat' is not defined
پارامترها هم مانند متغیرهای داخل توابع ، محلی هستند . برای مثال خارج از تابع print_twice ، هیچ چیزی بنام name وجود نداره .
توابع دارای مقدار برگشتی و توابع خالی(fruitful functions and void functions) :
بعضی از توابعی که قبلا استفاده کردیم مانند توابع ریاضی ، نتایجی رو برای ما برگشت میدادن . به این نوع توابع در اینجا اصطلاحا fruitful functions خواهیم گفت. در مقابل ، توابعی مانند print_twice عملی رو برای ما انجام میدادن بدون اینکه مقداری رو برای ما برگشت بدن . به این نوع توابع ، void functions میگیم.
وقتی ما یک تابعِ fruitful رو فراخوانی میکنیم ، قصد داریم که از مقدار برگشتی اون برای انجان یک کاری استفاده کنیم. برای مثال میخوایم که مقدار برگشتی رو داخل یک متغیری بریزیم و یا اینکه مقدار برگشتی به عنوان بخشی از یک عبارت مورد استفاده قرار بگیره :
x = math.cos(radians)
golden = (math.sqrt(5) + 1) / 2
وقتی که ما تابعی رو داخل مفسر پایتون فراخوانی میکنیم ، مقدار برگشتی برای ما نمایش داده میشه :
>>> math.sqrt(5)
2.2360679774997898
ولی در حالت اسکریپتی وقتی تابعی رو فراخوانی میکنیم که دارای مقدار برگشتی هست ، مقدار برگشتی برای همیشه از دست خواهد رفت :
math.sqrt(5)
اسکریپت بالا ریشه ی دوم عدد 5 رو محاسبه میکنه ولی چون نه مقدار برگشتی رو داخل متغیری میریزه و نه مقدار رو برای ما نمایش میده ، زیاد به درد بخور نیست :)
توابع Void میتونن مقداری رو برای ما نمایش بدن و یا تاثیرات دیگه ای داخل برنامه داشته باشن ولی هیچ مقداری رو برای برنمیگردونن ؛ اگر با توابع void مانند توابع fruitful رفتار کنیم (یعنی مقدار برگشتی رو داخل متغیری بریزیم) با نوع خاصی از مقدار بنام None مواجه خواهیم شد :
>>> result = print_twice('Bing')
Bing
Bing
>>> print(result)
None
مقدار None مشابه مقدار رشته ای "None" نیست و نوع مخصوص خودش رو در زبان پایتون داره :
>>> print(type(None))
<class 'NoneType'>
چرا از توابع استفاده میکنیم ؟
- ایجاد توابع به ما این امکان رو میده که دستورات برنامه رو بصورت گروهی نام گذاری کنیم که این کار خوانایی و خطایابی برنامه رو راحت تر میکنه.
- استفاده از توابع باعث حذف کدهای تکراری و کاهش حجم کدهای برنامه میشه ؛ که این کار سبب میشه تغییرات رو راحت تر بتونیم اعمال بکنیم.
- تقسیم یک برنامه به توابع این امکان رو به ما میده که بخش های مختلف برنامه رو بصورت جداگانه خطایابی کنیم.
- توابعی که به خوبی طراحی و پیاده سازی شدن ، میتونن در بسیاری از برنامه ها و پروژه های دیگه دوباره استفاده بشن.
تمرین ها :
- تابع average رو طوری تعریف کنید که دو عدد رو به عنوان پارامتر دریافت کنه و میانگین اونها رو برای ما نمایش بده.
- تابع right_justify رو طوری تعریف کنید که پارامتری بنام s از نوع رشته رو بگیره و با رعایت فاصله ی کافی از سمت چپ ، رشته ی مورد نظر رو طوری چاپ بکنه که حرف آخر رشته در ستون 70 ام صفحه نمایش قرار گرفته باشه. (راهنمایی : با استفاده از تلفیق و تکرار رشته ها و تابع len میتونید این تمرین رو حل کنید)
خب اینم از فصل سوم ، امیدوارم مفید واقع بشه.
موفق باشید :)
مطلبی دیگر از این انتشارات
سیر تا پیاز رتبهبندی در شرکتهای انفورماتیک
مطلبی دیگر از این انتشارات
آموزش شی گرایی در پایتون - قسمت دوم
مطلبی دیگر از این انتشارات
مقدمه ای برای شروع یادگیری زبان برنامه نویسی PHP