گاهی نیاز است تا قطعه کدی که یک وظیفه مشخص را انجام می دهد در بخشهای مختلف یک برنامه ، بارها اجرا شود. برای مثال در سامانه مدیریت تحصیلی دانشگاه ، قطعه برنامه محاسبه میانگین نمرات دانشجویان به هنگام پردازش کارنامه پایان ترم ، محاسبه میزان تخفیف شهریه ویا تعیین تعداد واحدهایی که دانشجویان می توانند در آخرین نیم سال تحصیلی بردارند اجرا می شود. نیاز به اجرای چند باره یک قطعه کد واحد در بسیاری از برنامه های بزرگ که برای حل مسائل دنیای واقعی نوشته می شوند وجود دارد. و باید به خوبی تدبیر گردد. از دیگرسو برای نوشتن یک برنامه بزرگ و پیچیده با چندین وظیفه مختلف می توان آن را به چند زیر برنامه کوچک که هریک وظیفه مشخصی را انجام می دهند شکست و هر وظیفه را جداگانه کد نویسی و پیاده سازی کرد. این روش ، اشکال زدایی نرم افزار و آزمون درستی کارکرد آن را ساده تر می کند. برای مثال سامانه بانکداری ، یک نرم افزار بزرگ و پیچیده است که می تواند به بخش های کوچکتری چون : افتتاح حساب ، واریز ، برداشت ، انتقال ، حواله ، بستن حساب و دیگر وظایف موجود در حوزه بانکداری شکسته شود.بنا بر آنچه گفته شد در زبان های برنامه نویسی باید راهی برای دوری گزیدن از نوشتن کدهای تکراری و نیزامکان تجزیه یک مسئله بزرگ و پیچیده به چندین مسئله کوچک و ساده تر وجود داشته باشد ، بایستگی برآوردن این دو نیاز، سرآغاز برآمدن یک مفهوم بسیار مهم و اساسی در زبانهای برنامه نویسی است که از آن با نام تابع (function) یاد می شود.
یک تابع ، قطعه برنامه کوچکی است که برای انجام یک کار مشخص نوشته می شود و دارای یک نام است و می تواند در بخش های مختلف برنامه اصلی با استفاده از نام آن ، بارها فراخوانی گردد. به بیان ساده تر ، تابع برنامه کوچکی است که قابلیت استفاده مجدد دارد یعنی برنامه نویس تنها یکبار آن را می نویسد. و بارها و بارها در بخش های مختلف برنامه اصلی و تنها با نوشتن یک نام (به جای نوشتن دوباره قطعه کد) آن را فرا می خواند. یک تابع ، داده های مورد نیاز خود را از برنامه فراخوان دریافت می کند و با انجام پردازش روی آنها نتیجه به دست آمده را به برنامه فراخوان برمی گرداند. از این دیدگاه تابع همانند دستگاهی است که می تواند با انجام عملیات بر روی ورودی های خود ، یک خروجی تولید کند.
پیامدهای سودمند بهره گیری از تابع در برنامه نویسی :
1- پیشگیری از تکرار کد
2- بهره گیری دوباره از کدی که تنها یک بار نوشته شده است.
3- اشکال زدایی از کد وآزمون درستی کارکرد آن بهتر و سریع تر انجام می شود.
4- نگهداری ساده تر از برنامه
5- شکستن یک برنامه بزرگ و پیچیده به چندین برنامه کوچک و ساده تر
6- مفهوم متغیرهای محلی در تابع ، مدیریت فضای نام برنامه را بسیار آسان می سازد.
7- بهبود خوانایی برنامه و درک ساده تر آن
8- ساخت یافتگی کد برنامه افزایش یافته و نظم درونی و ارتباط منطقی حاکم بر بخشهای مختلف نرم افزار بیشینه می گردد.
در زبان برنامه نویسی پایتون ، یک تابع با رهنمون def تعریف می شود و دارای چهار بخش اصلی است : نام تابع ، ورودی های تابع ، بدنه تابع و خروجی تابع
1- نام تابع
نام تابع که پس از رهنمون def قرار می گیرد. باید از قوانین نامگذاری متغیرها پیروی کند و بهتر است بیانگر هدف تابع و نشان دهنده کاری باشد که تابع انجام می دهد. برای مثال اگر کار تابع رسم یک دایره است نام DrawCircle زیبنده آن خواهد بود. همیشه پس از نام تابع یک جفت کمانک باز و بسته () قرار خواهد گرفت. در زبان برنامه نویسی پایتون فراخوانی یکتابع بااستفاده از نام تابع به همراه کمانک بازوبسته پس از آن انجام می شود.
مثال : بخش نام در تابعی که مساحت یک مستطیل را محاسبه می کند می تواند همانند الگوی زیر باشد :
def calc_rect_erea ()
2- ورودی های تابع
به داده های خام مورد نیاز تابع که بایدتوسط برنامه فراخوان در دسترس تابع قرارگیرند ورودی های تابع گفته می شود. یک تابع می تواند هیچ و یا چندین ورودی داشته باشد. ورودی های مورد نیاز یک تابع پس از نام تابع و درون جفت کمانک بازو بسته () قرار می گیرند. در فرهنگ واژگان زبان های برنامه نویسی به ورودیهای یک تابع که به هنگام تعریف آن و در قالب یک یا چند متغیر، مشخص می شوند ورودی های ظاهری یا پارامتر[1] گفته می شود و به داده هایی که به هنگام فراخوانی تابع به آن فرستاده می شوند و جانشین پارامترهای تعریف شده درتابع میشوند آرگومان [2]یا ورودی واقعی گفته می شود.
با وجود آنکه بسیاری از برنامه نویسان واژه های Parameter و Argument را به جای یکدیگر بکار می برند اما چنان که گفته شد هریک از این دو متفاوت با دیگری است و بکارگیری این دو در معنای یکسان شایسته یک برنامه نویس خبره نیست. در حقیقت ورودی های مجازی یا پارامتر به متغیرهایی گفته می شود که در زمان تعریف یک تابع درون جفت کمانک بازوبسته پس از نام آن قرار می گیرند در حالی که ورودی واقعی یا آرگومان اشاره به داده هایی دارد که به هنگام فراخوانی تابع، در دسترس تابع قرار گرفته و جانشین پارامترهای تعریف شده در آن می شوند.
مثال : تابع calc_rect_erea () که وظیفه محاسبه مساحت یک مستطیل را دارد نیاز به دانستن طول و عرض مستطیل دارد بنابراین برنامه فراخوان باید طول و عرض مستطیل مورد نظررا در دسترس این تابع قرار دهد پس این تابع باید دو ورودی داشته باشد که یکی طول مستطیل (length) است و دیگری عرض مستطیل (width) از این رو تا این جای کار تعریف تابع همانند زیر خواهد بود :
def calc_rect_erea (length , width)
مثال : تابع get_now() که تاریخ و زمان جاری را بر می گرداند نیاز به هیچ ورودی ندارد از این رو الگوی بخش نام و پارامترهای آن همانند زیر خواهد بود :
def Get_now()
مثال : تابع ()MiladyToShamsi که وظیفه تبدیل تاریخ میلادی به شمسی را بر عهده دارد به سه ورودی سال میلادی ، ماه میلادی و روز نیاز دارد پس الگوی نام و پارامترهای آن همانند زیر خواهد بود :
MiladyToShamsi(year,month,day)
3- بدنه تابع
برنامه کوچکی که وظیفه اصلی تابع را انجام می دهد در بدنه تابع قرار می گیرد. کدهای موجود در بدنه تا زمانی که تابع فراخوانی نگردد اجرا نخواهند شد. دستورات موجود در بدنه تابع پس از علامت دو نقطه بیانی : قرار می گیرند و باید با استفاده از یک و یا چندین فاصله نسبت به رهنمون def تورفتگی داشته باشند مقدار تورفتگی را برنامه نویس به دلخواه انتخاب می کند اما باید تا پایان قطعه کد بدنه ثابت باقی بماند بیشتر برنامه نویسان برای ایجاد تورفتگی از چهار فاصله خالی استفاده می کنند. بدنه تابع با اولین تورفتگی نسبت به رهنمون def آغاز و با اولین دستور بدون تورفتگی پایان می یابد
مثال : می دانیم که مساحت یک مستطیل برابر است با حاصلضرب اندازه طول آن در اندازه عرض آن پس کدی که در بدنه تابع calc_rect_erea () قرار میگیرد همانند الگوی زیر خواهد بود :
def calc_rect_erea (length , width) :
erea = length * width
در پایتون بدنه یک تابع هرگز نمی تواند خالی باشد از این رو چنانچه به هردلیلی نیاز به داشتن چنین تابعی دارید برای پیشگیری از اعلام خطا توسط مفسر پایتون می توانید از یک دستور pass در بدنه تابع استفاده کنید. این دستور هیچ کاری انجام نمی دهد اما از دید مفسر پایتون یک دستور به شمار می رود و در نتیجه مانع از اعلام خطا می گردد.
مثال :
def myfunction():
pass
4- خروجی تابع
یک تابع می تواند هیچ خروجی نداشته باشد و یا تنها یک خروجی داشته باشد. خروجی تابع با دستور return که در بدنه تابع قرار می گیرد به برنامه فراخوان برگردانده می شود. مفسر پایتون با رسیدن به دستور return خروجی تابع را به برنامه فراخوان تحویل داده و به اجرای تابع پایان می دهد. با پایان یافتن اجرای تابع ، اجرای برنامه اصلی از نخستین خط پس از تابع از سر گرفته می شود.
دستورreturn به تنهایی یعنی زمانی که مقدار یا عبارتی پس از آن نوشته نشودمقدار None را برمی گرداند همچنین اگر در انتهای تابع دستور return نوشته نشود، مفسر پایتون به صورت ضمنی دستور return None را برای آن در نظر خواهد گرفت. بنابراین با فراخوانی این گونه توابع وپس از اجرای کامل دستورات داخل بدنه مقدار None بازگردانده خواهد شد.
مثال : در تابع calc_rect_erea () خروجی تابع مساحت محاسبه شده یک مستطیل است پس بدنه تابع که دارای دستور return است همانند زیر نوشته می شود :
def calc_rect_erea (length , width) :
erea = length * width
return erea
مثال : تابع زیرتنها یک پیام "Hello World" چاپ خواهد کرد بنابراین هیچ خروجی به برنامه فراخوان برنمی گرداند از این رو نیازی به دستور return ندارد :
def say_hello ():
print("Hello World ")
در زبان برنامه نویسی پایتون فراخوانی یک تابع با استفاده از نام تابع و یک جفت کمانک بازوبسته () که پس از نام تابع قرار می گیرد انجام می شود.
مثال : در برنامه زیر ابتدا طول و عرض یک مستطیل از کاربر گرفته می شود و سپس ورودی های فراهم شده توسط کاربر در دسترس تابع calc_rect_erea() قرار میگیرند تا مساحت مستطیل محاسبه گردد و سرانجام خروجی این تابع در متغیر RectArea قرار گرفته و با چاپ مقدار این متغیر برنامه اصلی پایان می یابد همانگونه که در کد برنامه دیده می شود تابع calc_rect_erea() دارای دو پارامتر به نام های length وwidth است که به هنگام تعریف تابع مشخص شده اند و در زمان فراخوانی دو آرگومان w و h به تابع ارسال شده است :
def calc_rect_erea (length , width) :
erea = length * width
return erea
w = float(input("pleas Enter Rect width :"))
h = float(input("pleas Enter Rect length:"))
RectArea = calc_rect_erea (w , h)
print(RectArea)
نکاتی چند در باره پارامتر و آرگومان :
1- تعداد آرگومان های ارسالی به یک تابع در هنگام فراخوانی آن باید برابر با تعداد پارامترهایی باشد که در زمان تعریف تابع مشخص شدهاند وگرنه مفسر پایتون اعلام خطا خواهد کرد.
مثال : در کد زیر تابع add() دارای سه پارامتر a , b , c است اما به هنگام فراخوانی تنها دو آرگومان دریافت کرده است بنابراین مفسر پایتون خطای زیر را اعلام می کند :
missing 1 required positional argument: c
def add (a, b, c) :
s = a + b + c
return s
num1 = 10
num2 = 15
num3 = 5
rslt = add (num1, num2)
print(rslt)
2- ارسال آرگومانها به تابع باید به همان ترتیب قرار گرفتن پارامتر ها در تعریف تابع باشد برای مثال اگر تابع add() همانند زیر تعریف شود مفسر پایتون به هنگام فراخوانی تابع add() نخستین آرگومان را جایگزین پارامتر a ، دومین آرگومان را جایگزین پارامتر b و سومین آرگومان را جایگزین پارامتر c کرده و سپس کد موجود در بدنه تابع را اجرا می کند.
def add (a, b, c) :
return a + b + c
s = add (5 , 7 , 12)
print ( s )
گاهی تعداد آرگومان هایی که می توان به هنگام فراخوانی یک تابع به آن ارسال کرد نمی تواند ثابت باشد و براساس شرایط برنامه و نیاز کاربرممکن است تغییر کند برای مثال آرگومان های تابع weight_average() که میانگین وزن دانش آموزان پایه دوم ابتدایی مدارس موجود در مناطق روستایی دارای کمتر از 35 خانوار را محاسبه می کند نمیتواند ثابت باشد چرا که تعداد دانش آموزان پایه دوم ابتدایی از یک مدرسه تا مدرسه دیگر متفاوت است.
درپایتون برای فرستادن تعداد نامشخصی آرگومان به یک تابع ، باید به هنگام تعریف تابع یک علامت ستاره ( * ) پیش از نام پارامتری که می خواهید آرگومان های نامحدود ارسال شده به تابع جایگزین آن شوند قرار دهید. با این کار به مفسر پایتون می گوییم که برنامه فراخوان می تواند تعداد نامحدودی آرگومان به این تابع ارسال کند. درحقیقت دراین حالت مفسر پایتونآرگومان های ارسال شده به تابع را در در قالب ساختمان داده tuple جایگزین پارامتر مورد نظر می کند بنابراین در بدنه تابع می توان با پارامتر یاد شده همانند یک tuple رفتار کرد برای مثال جهت دسترسی به هر یک از آرگومان های نامحدود کافی است شاخص آرگومان مورد نظر درون عملگر انتخاب ( جفت علامت [] پس از نام پارامتر) قرارگیرد و برای یافتن تعداد آرگومان های ارسال شده به تابع کافی است از دستور len(paramname) استفاده کنید.
مثال : تابعی بنویسید که بتواند تعداد نامحدودی آرگومان دریافت کرده و تعداد آرگومان های دریافتی را به برنامه فراخوان برگرداند.
def myfunc (*myparam) :
count = len(myparam)
return count
print (myfunc (5,3,10,14,15))
print (myfunc (7, 8))
print (myfunc (71, 18, 15, 14, 19,33 ,62,10, 19, 15.8, 16.7))
مثال : تابعی بنویسید که بتواند تعداد نامحدودی آرگومان عددی دریافت کرده و حاصل جمع آرگومان اول و دوم خود را به برنامه فراخوان برگرداند.
def myfunc (*mynumbers) :
return mynumbers [0] + mynumbers [1]
print (myfunc (5,3,10,14,15))
print (myfunc (7, 8))
print (myfunc (71, 18, 15, 14, 19,33 ,62,10, 19, 15.8, 16.7))
مثال : تابعی بنویسید که بتواند میانگین تعداد نا مشخصی عدد که به عنوان ورودی به آن داده می شوند را محاسبه کند.
def numbers_average (*number):
count = len(number)
s = 0
for i in range(count):
s += number[i]
avrg = s / count
return avrg
print (numbers_average (5,3,10,14,15))
print (numbers_average (7, 8))
print (numbers_average (71, 18, 15, 14, 19,33 ,62,10, 19, 15.8, 16.7))
در حالت عادی ، ارسال آرگومانها به تابع باید به همان ترتیب قرار گرفتن پارامتر ها در تعریف تابع باشد برای مثال اگرتابع add() همانند زیر تعریف شود مفسر پایتون به هنگام فراخوانی تابع add() نخستین آرگومان را جایگزین پارامتر a ، دومین آرگومان را جایگزین پارامتر b و سومین آرگومان را جایگزین پارامتر c کرده و سپس کد موجود در بدنه تابع را اجرا می کند.
def add (a, b, c) :
return a + b + c
s = add (5 , 7 , 12)
print ( s )
در پایتون میتوانآرگومان های یک تابع را با استفاده از نام پارامتر مورد نظر به تابع ارسال کرد دراین روش نیازی به رعایت ترتیب قرار گرفتن پارامترها نیست. به این دسته از ورودی های واقعی[3] آرگومانهای نامدار گفته می شود. شیوه کلی نگارش و بکار گیری آرگومانهای نامدار به هنگام فراخوانی یک تابع همانند الگوی زیر است :
FunctionName(ParametrNmame = Argument)
مثال :
def add (a, b, c) :
return a + b + c
s = add (b = 14 , a = 7 , c = 15)
print ( s )
مثال : تابع محاسبه حجم یک مکعب و ارسال آرگومان به روش با نام به آن :
حجم مکعب حاصلضرب طول (length) در عرض (width) در ارتفاع (height) آن است
def cube_mass (length, width, height) :
return length * width * height
cube = cube_mass (height = 8 , length = 10 , width = 7)
print ( cube )
به هنگام فراخوانی توابع میتوان همزمان از دو روش ارسال آرگومان با نام و عادی (ترتیبی) استفاده کرد به شرطی که آرگومان های ترتیبی پیش از آرگومان های نامدار و به همان ترتیب قرار گیری در تعریف تابع ارسال شوند.
مثال : فراخوانی تابع cube_mass() که در مثال قبلی تعریف شده است با ترکیبی از آرگومان های نامدار و عادی
cube = cube_mass ( 6 , 10 , height = 7)
گاهی تعداد آرگومان های نامداری که می توان به هنگام فراخوانی یک تابع به آن ارسال کرد نمی تواند ثابت باشد و براساس شرایط برنامه و نیاز کاربرممکن است تغییر کند برای مثال آرگومان های ارسالی به تابع student_info_process() که وظیفه پردازش اطلاعات شناسنامهای دانش آموزان ابتدایی مدارس شهرستان بجنورد را دارد نمیتواند ثابت باشد چرا که ثبت اطلاعاتی چون نام ، نام خانوادگی ، نام پدر و شناسه ملی دانش آموز در پرونده تحصیلی اجباری است اما ثبت اطلاعاتی مانند شغل پدر ، وزن و قد دانش آموزان اجباری نیست. و از سویی هر مدرسه به فراخور برنامه ها و امکانات خود می تواند اطلاعات بیشتری در مورد هر دانش آموز در پرونده تحصیلی ثبت و نگهداری کند.
در پایتون برای ارسال تعداد نامشخصی آرگومان نامدار به یک تابع ، باید به هنگام تعریف تابع یک علامت دوستاره (**) پیش از نام پارامتر مورد نظر قرار دهید. با این کار به مفسر پایتون میگوییم که برنامه فراخوان می تواند تعداد نامحدودی آرگومان نامدار به این تابع ارسال کند. در حقیقت در این حالت مفسر پایتون آرگومان های نامدار ارسال شده را در قالب ساختمان داده dictionary به تابع ارسال می کند بنابراین در بدنه تابع می توان با پارامتر یاد شده همانند یک dictionary رفتار کرد
مثال :
def student_info_process(studentid, **kwargs):
for k in kwargs:
print( k + " : " + kwargs[k])
student_info_process(100, Name="Arash", Family="Izanlou" ,
Father="Mohammad" ,Code="0681234567")
خروجی کد بالا همانند زیر است :
Name : Arash
Family : Izanlou
Father : Mohammad
Code : 0681234567
تا اینجا می دانیم که هرگاه برنامه ما تابعی را برای اجرا فراخوانی کند مفسر پایتون دو روش برای انطباق آرگومان های ارسالی به تابع با پارامترهای تعریف شده در آن در اختیار دارد :
روش عادی یا ترتیبی که ارسال آرگومانها به تابع باید به همان تعداد و به ترتیب قرار گرفتن پارامتر ها در تعریف تابع باشد و نیز روش آرگومان های نامدار که آرگومان های یک تابع با استفاده از نام پارامتر مورد نظر و به صورت مقدار= نام پارامتر به تابع ارسال می شوند در این روش نیازی به رعایت ترتیب قرار گرفتن پارامترها نیست.
همچنین می دانیم که به هنگام فراخوانی توابع میتوان ترکیبی از آرگومان های نامدار و عادی (ترتیبی) را مطابق تعریف تابع به آن ارسال کرد. به شرط آنکه آرگومان های عادی پیش ازآرگومان های نامدار و به همان ترتیب قرار گیری در تعریف تابع ارسال شوند.
میتوان برنامه فراخوان را وادار کرد تا ارسال آرگومان به برخی از پارامترهای یک تابع را فقط به شیوه آرگومان های نامدار انجام دهد یعنی ارسال آرگومان به آنها به روش عادی(ترتیبی) غیر فعال گردد به این منظور باید به هنگام تعریف تابع و پیش از نام پارامترهای مورد نظر از نویسه* به عنوان یک پارامتر نشانگر استفاده کرد در این حالت تمامی آرگومانهای متناظر با پارامترهای بعد از * باید به صورت نامداریعنی به شکل مقدار = نام پارامتر ارسال شوند.
همچنین می توان برنامه فراخوان را وادار ساخت تا ارسال آرگومان به برخی از پارامترهای یک تابع را فقط به روش عادی(ترتیبی) یعنی بر اساس موقعیت پارامتر های آن انجام دهد یعنی امکان ارسال آرگومان به روش نامدار و به شکل مقدار= نام پارامتر برای آنها غیرفعال شود. برای دست یابی به این هدف باید به هنگام تعریف تابع و پس از نام پارامترهای مورد نظر نویسه / را به عنوان یک پارامتر نشانگر وارد کنیم. برای مثال در تابع f که به شکل زیر تعریف شده است آرگومان های ارسالی به پارامترهای pos1 و pos2 باید به صورت عادی(ترتیبی) باشند اما آرگومان ارسالی به پارامترpos_or_kwd میتواند به هردو صورت عادی و نامدار باشد درحالی که آرگومان های متناظر با هریک از پارامترهایkwd2 وkwd1 تنها باید به شکل نامدار ارسال شوند.
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
pass
مثال :
def standard_arg(arg):
print(arg)
def pos_only_arg(arg, /):
print(arg)
def kwd_only_arg(*, arg):
print(arg)
def combined_example(pos_only, /, standard, *, kwd_only):
print(pos_only, standard, kwd_only)
standard_arg(2)
standard_arg(arg=2)
combined_example(1, 2, kwd_only=3)
combined_example(1, standard=2, kwd_only=3)
در پایتون هر پارامتر می تواند یک مقدار پیش فرض داشته باشد ، این مقدار به هنگام تعریف تابع و با استفاده از عملگر انتساب (=) به پارامتر مورد نظر داده می شود. و چنانچه به هنگام فراخوانی تابع ، آرگومانی به این پارامتر ارسال نگردد مفسر پایتون همان مقدار پیش فرض را جایگزین پارامتر خواهد کرد.
مثال : دربرنامه زیر هر سه پارامتر تابع ()cube_mass دارای مقدار پیش فرض هستند.
def cube_mass (length = 10 , width = 8, height = 5) :
return length * width * height
cube = cube_mass (height = 8 , length = 10 , width = 7)
print ( cube )
cube = cube_mass (9 , 11 ,8)
print ( cube )
cube = cube_mass (15)
print ( cube )
cube = cube_mass (width = 6)
print ( cube )
cube = cube_mass ()
print ( cube )
در باره پارامترهای پیش فرض دو نکته بسیار مهمی که لازم است همواره به یاد داشته باشید این است که :
1- مقدار پیش فرض در نقطه تعریف تابع ارزیابی می شود و نه به هنگام فراخوانی آن
مثال : برای مثال کد زیر عدد 5 را چاپ خواهد کرد نه عدد 6 را
i = 5
def func(arg=i):
print(arg)
i = 6
func()
2- مقدار پیش فرض تنها یک بار ارزیابی می شود.
مثال :
def f(a, L=[]):
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
خروجی مثال بالا همانند زیر است :
[1]
[1, 2]
[1, 2, 3]