در آموزش گذشته با نحوۀ تعریف و فراخوانی فانکشنهای ساده آشنا شدیم و دیدیم که چگونه از تکرار کدها جلوگیری میکنند. در واقع، همانطور که پیش از این اشاره کردیم، فانکشنها همچون دستگاهی عمل میکنند که گاهی برای راهاندازی و انجام کار مد نظر نیاز به دریافت ورودی دارند و دستگاه آبمیوهگیری را به عنوان مثالی در این زمینه بیان کردیم. فانکشنهایی که با پارامتر ورودی تعریف میشوند، علاوه بر جلوگیری از تکرار کدها، انعطافپذیری ارائۀ یکسری خروجی متفاوت را دارند به طوری که بر اساس دریافت ورودیها یا آرگومانهای مختلف، خروجی مربوطه را در اختیارمان قرار میدهند که در این آموزش با نحوۀ تعریف و فراخوانی این دست فانکشنها آشنا خواهیم شد.
به منظور درک بهتر این موضوع، فانکشنی تعریف میکنیم که در هر بار فراخوانی نام کاربر را به عنوان آرگومان ورودی گرفته و یک پیغام خوشامدگویی همراه با نام کاربر مذکور در خروجی چاپ کند که برای این منظور برنامۀ زیر را در فایلی به نام greetUserFunc.py
ذخیره کرده و اجرا میکنیم:
def greetUser(name):
print("Hello dear", name, ",")
print("Welcome to SokanAcademy.com")
greetUser('Ali')
greetUser('Sara')
greetUser('Amir')
همانطور که میبینید، فانکشنی تحت عنوان ()greetUser
تعریف کرده و پارامتر ورودی به نام name
را برای آن در نظر گرفتهایم و بدین ترتیب در صورت فراخوانی این فانکشن، آرگومان ورودی به پارامتر مذکور ارجاع داده شده و در بدنۀ فانکشن مورد استفاده قرار میگیرد که در آن گفتهایم در صورت فراخوانی، استرینگ مربوط به نام کاربر را به عنوان آرگومان ورودی دریافت کرده و با استرینگ «Hello dear» کانکت کرده و در خروجی چاپ کند و در سطر بعد نیز گفتهایم استرینگ «Welcome to SokanAcademy.com» در ادامه چاپ شود.
در ادامه، فانکشن ()greetUser
را سه مرتبه پشت سر هم و با آرگومانهای ورودی متفاوت فراخوانی میکنیم که در هر مرتبه استرینگ مد نظر به متغیر name
منتسب شده و به همراه استرینگ «Hello dear» در خروجی چاپ میشود و به دنبال آن نیز استرینگ «Welcome to SokanAcademy.com» در سطر بعدی چاپ میشود که در نهایت خروجی حاصل از اجرای کد فوق بدین ترتیب خواهد بود:
Hello dear Ali ,
Welcome to SokanAcademy.com
Hello dear Sara ,
Welcome to SokanAcademy.com
Hello dear Amir ,
Welcome to SokanAcademy.com
همانطور که میبیند، سه آرگومان ورودی متفاوت به فانکشن ()greetUser
دادهایم که در هر مرتبه استرینگ متناسب با نام کاربر ورودی در خروجی چاپ شده است.
نکته |
پارامترهای ورودی در هر فانکشن متغیری هستند که همچون تمامی متغیرها بهتر است نامهایی اصولی و معنادار برایشان انتخاب شود به طوری که شناسۀ منتسب به آنها نشاندهندۀ ماهیت آرگومانهای ورودی به این فانکشنها باشند. |
به عنوان یک مثال از نامگذاری اصولی متغیرها میتوانیم فانکشنی را در نظر بگیریم که چند آرگومان ورودی فرضی همچون نام و رنگ میگیرد و یکسری پردازش روی آنها انجام میدهد که در این مورد برای نامگذاری پارامترهای ورودی میتوان به ترتیب از شناسههایی مانند color
و name
استفاده کرد. البته توجه داشته باشیم که یک فانکشن میتواند به تعداد دلخواه و مورد نیاز پارامتر ورودی بگیرد که در این مورد فرم کلی بخش هِدِر فانکشن به صورت زیر خواهد بود:
def functionName(par1, par2, par3, ..., parN)
همانطور که میبینید، نام تمامی پارامترهای ورودی در بین پرانتزهای جلوی شناسۀ فانکشن قرار میگیرند و با علامت ,
از هم جدا میشوند و توجه داشته باشیم که تعداد پارامترهای ورودی در هر فانکشن نباید از یک حد معقول فراتر رود چرا که تعداد زیاد پارامترها منجر به پیچیدگی فانکشن خواهد شد. برای مثال، فانکشن زیر را مد نظر قرار میدهیم:
def times(value1, value2):
print(value1 * value2)
در کد فوق، فانکشن ()times
با دو پارامتر ورودی تعریف شده است که در هنگام فراخوانی چنین فانکشنهایی رعایت یکسری نکات ضروری میباشد که در ادامه به بررسی آنها میپردازیم.
نکتۀ اول در مورد فانکشنهایی با پارامترهای ورودی این است که در فراخوانی چنین فانکشنهایی تعداد آرگومانهای ورودی به آنها باید دقیقاً برابر با تعداد پارامترهای ورودیِ تعریفشده باشد که در غیر این صورت مفسر پایتون اعلام خطا خواهد کرد. به عنوان مثال، تصویر زیر را در نظر میگیریم که در آن فانکشن ()times
مربوط به مثال قبل را با تعداد آرگومان ورودی متفاوت فراخوانی کردهایم:
همانطور که میبینید، تنها در صورتی فانکشن ()times
به درستی اجرا شده است که دو آرگومان ورودی به آن دادهایم که دقیقاً برابر با تعداد پارامترهای ورودی در هنگام تعریف فانکشن مربوطه است.
به طور کلی، میتوان گفت که قانون فوقالذکر در مورد فراخوانی فانکشنهایی صادق است که خودمان آنها را تعریف کردهایم بدین معنی که در هنگام فراخوانی چنین فانکشنهایی باید توجه داشته باشیم که تعداد آرگومانهای ورودی به آنها برابر با تعداد پارامترهای در نظر گرفته شده برای اجرای صحیح فانکشن باشد اما این در حالی است که فراخوانی فانکشنهای به اصطلاح Built-in همچون ()print
با تعداد آرگومان ورودی دلخواه امکانپذیر است که برای درک بهتر این موضوع مثال زیر را در ادامه آوردهایم:
>>> print()
>>> print("Sokan")
Sokan
>>> print("Sokan", "Academy")
Sokan Academy
در کد فوق ابتدا فانکشن ()print
را بدون هیچگونه آرگومان ورودی فراخوانی کردهایم که منجر به چاپ یک استرینگی تهی شده است و در ادامه این فانکشن را با یک آرگومان ورودی دلخواه همچون استرینگ «Sokan» فراخوانی کردهایم که استرینگ مذکور در خروجی چاپ شده است و در سطر بعد نیز این فانکشن با دو پارامتر ورودی فراخوانی شده است که در نهایت استرینگهای ورودی با هم کانکت شده و در خروجی چاپ شدهاند.
نکتۀ دوم در مورد فانکشنهایی است که بیش از یک پارامتر ورودی دارند که در چنین فانکشنهایی از دو روش به منظور ارجاع آرگومانهای ورودی به پارامترها میتوان استفاده کرد که در ادامه هر یک را توضیح میدهیم:
روش اول اینکه آرگومانهای ورودی به ترتیب به فانکشن داده میشوند و مانند پارامترهای ورودی با علامت ,
از هم جدا میشوند که در این حالت مفسر پایتون از سمت چپ شروع کرده و آرگومان اول را به پارامتر اول، آرگومان دوم را به پارامتر دوم و به همین ترتیب با پیشروی به سمت راست هر آرگومان را به پارامتر متناظرش ارجاع میدهد که به عنوان مثالی در این مورد فانکشنی تحت عنوان ()addition
تعریف میکنیم:
>>> def addition(value1, value2):
print(value1, "+", value2, "=", value1 + value2)
حال دو عدد 3 و 5 را به عنوان آرگومان ورودی به فانکشن فوق داده و آن را فراخوانی میکنیم:
>>> addition(3, 5)
3 + 5 = 8
همانطور که میبینید، به ترتیب عدد 3 به عنوان آرگومان اول به پارامتر اول تحت عنوان value1
اختصاص یافته و عدد 5 به عنوان آرگومان دوم به پارامتر دوم تحت عنوان value2
ارجاع داده شده است و در ادامه هر یک از اعداد مذکور در بدنۀ فانکشن و در دستور پرینت مورد استفاده قرار گرفتهاند.
روش دوم اینکه برای ارجاع آرگومانهای ورودی به پارامترها از نام متغیر میتوان استفاده کرد بدین صورت که مقدار دلخواه برای هر پارامتر ورودی با عملگر =
به آرگومان مد نظر منتسب میشود که در این روش رعایت ترتیب انتسابها ضرورتی ندارد چرا که به طور مشخص آرگومان ورودی به پارامتر مد نظر ارجاع داده میشود. برای مثال، کد مربوط به فانکشن ()addition
در نظر میگیریم:
>>> addition(value2 = 5, value1 = 3)
3 + 5 = 8
همانطور که میبینید، با وجود آن که پارامتر دوم پیش از پارامتر اول مقداردهی شده است اما باز هم خروجی فانکشن مانند حالت قبل میباشد اما سؤالی که در اینجا پیش میآید این است که «آیا میتوان تنها برخی از آرگومانهای ورودیِ فانکشن را با بهکارگیری شناسۀ پارامتر آن مقداردهی کرد؟» که در پاسخ به این سؤال، مثال زیر را مد نظر قرار میدهیم:
>>> addition(3, value2 = 5)
3 + 5 = 8
در کد فوق، مقدار مربوط به آرگومان اول از فانکشن ()addition
را برابر با عدد 3 قرار دادهایم و در ادامه آرگومان دوم را به شناسۀ پارامتر دوم تحت عنوان value2
منتسب کرده و به فانکشن مذکور دادهایم که در این صورت حتماً باید ترتیب تعریفشده برای پارامترهای ورودی در تعریف فانکشن را رعایت کنیم. برای مثال، در کد زیر سعی کردهایم تا فانکشن ()addition
را بدون رعایت ترتیب پارامترهای ورودی فراخوانی کنیم:
>>> addition(value2 = 5, 3)
SyntaxError: positional argument follows keyword argument
همانطور که میبینید، اجرای کد فوق منجر به ارور شده است چرا که انتساب آرگومانهای ورودی به آن در هنگام فراخوانی با ترتیب پارامترهای تعریفشده برای فانکشن مذکور مطابقت ندارد.
البته بهتر است که از یک روش واحد برای آرگوماندهی به فانکشنها استفاده کنیم بدین معنی که تمامی آرگومانهای ورودی را به ترتیب تعریفشده برای پارامترهای فانکشن مد نظر به آن بدهیم و یا اینکه شناسۀ مربوط به پارامترهای ورودی را برای مقداردهی به کار بگیریم که البته استفاده از روش دوم برای مقداردهی آرگومانهای ورودی معایبی دارا است که از آن جمله میتوان به تغییر شناسۀ پارامترهای فانکشن مذکور اشاره کرد که در چنین شرایطی باید در تمام دستورات مربوط به فراخوانی این فانکشن شناسۀ مورد استفاده برای مقداردهی آرگومانهای ورودی را تغییر دهیم.
روشی سوم به منظور فراخوانی فانکشنهایی با پارامترهای ورودی بدین صورت میباشد که در تعریف فانکشن مد نظر مقداری پیشفرض به آرگومان ورودی اختصاص داده میشود که برای درک بهتر این روش مثال مربوط به فانکشن ()greetUser
را بدین صورت تغییر میدهیم:
def greetUser1(name = "User"):
print("Hello dear", name, ",")
print("Welcome to SokanAcademy.com")
greetUser1()
greetUser1("Reza")
در کد فوق، استرینگ پیشفرضِ «User» را به پارامتر ورودی name
اختصاص دادهایم که در صورت فراخوانی این فانکشن بدون آرگومان، مقدار پیشفرض به عنوان ورودی در نظر گرفته شده و دستورات بدنۀ فانکشن اجرا میشوند و از همین روی دستور سطر چهارم منجر بدین خواهد شد تا استرینگ «User» به همراه استرینگهای فوق در خروجی چاپ شود. در ادامه فانکشن ()greetUser1
را با آرگومان ورودی «Reza» فراخوانی کردهایم که بدین ترتیب استرینگ پیشفرضِ منتسب به متغیر name
پاک شده و استرینگ «Reza» جایگزین آن میشود و در ادامه به همراه استرینگهای فوقالذکر در خروجی چاپ میشود:
Hello dear User ,
Welcome to SokanAcademy.com
Hello dear Reza ,
Welcome to SokanAcademy.com
در بسیاری از زبانهای برنامهنویسی همچون جاوا نوع یک متغیر را باید پیش از تعریف آن تعیین کنیم اما این در حالی است که در پایتون یک متغیر را میتوان به هر نوع آبجکتی منتسب کرد و این موضوع در مورد پارامترهای ورودی یک فانکشن نیز صادق است. برای مثال فانکشن ()times
را مجدداً در نظر میگیریم و آرگومانهایی از انواع مختلف را به عنوان ورودی به آن میدهیم:
>>> times(10, 9)
90
>>> times('***', 3)
*********
همانطور که میبینید، در دستور اول آرگومانهای ورودی فانکشن ()times
هر دو از نوع عدد صحیح هستند اما در دستور فراخوانی دوم آرگومان اول از نوع استرینگ و آرگومان دوم از نوع عدد صحیح است که عملگر *
قابلیت اعمال روی هر نوع دادۀ ورودی را دارا است و بدین ترتیب دستورات مربوط به هر دو فراخوانی بدون مشکلی اجرا میشوند.
در برخی شرایط نیز ممکن است آرگومانهای ورودی به یک فانکشن منجر به بروز خطا در اجرای آن شود که برای مثال فانکشن ()addition
را در نظر میگیریم و چند آرگومان ورودی با دیتا تایپ متفاوت را به آن میدهیم:
>>> addition(10, 20)
10 + 20 = 30
>>> addition('Sokan', 'Academy')
Sokan + Academy = SokanAcademy
>>> addition('Sokan', 20)
Traceback (most recent call last):
File "<pyshell#36>", line 1, in
addition('Sokan', 20)
File "<pyshell#32>", line 2, in addition
print(value1,"+",value2,"=",value1+value2)
TypeError: Can't convert 'int' object to str implicitly
همانطور که ملاحظه میکنید، در فراخوانی اول و دوم به ترتیب آرگومانهایی از نوع عدد صحیح و استرینگ مورد استفاده قرار گرفتهاند که فانکشن مذکور بدون هیچ مشکلی اجرا شده و دو عدد صحیح را با هم جمع میکند و همچنین دو استرینگ ورودی را با یکدیگر کانکت کرده و نتیجه را در خروجی چاپ میکند اما دستور فراخوانی سوم منجر به ارور خواهد شد چرا که از آرگومانهای ورودی با دیتا تایپ متفاوت استفاده کردهایم و این در حالی است که عملگر +
قابلیت اِعمال روی نوع دادۀ استرینگ و عدد صحیح را ندارد؛ به عبارت بهتر، یک استرینگ را نمیتوان با عدد صحیح جمع کرد.
روی هم رفته، باید به این نکته توجه داشته باشیم که مفسر پایتون نوع دادۀ مربوط به آرگومان ورودی را بررسی نمیکند و این موضوع در برخی موارد منجر به بروز ارور خواهد شد که در مورد شیوههای کنترل و جلوگیری از چنین خطاهایی در فصول آینده صحبت خواهیم کرد.