در تمامی فانکشنهایی که تاکنون در مثالهای آموزشی این دوره تعریف کردهایم، تعداد پارامترهای ورودی از همان ابتدا ثابت و مشخص بود که در اکثر موارد نیز تعریف فانکشن در این زبان بدین روش انجام میشود و نیاز به توضیح نیست که رفع ارورهای احتمالی در چنین شرایطی به مراتب آسانتر میگردد اما این در حالی است که گاهی نمیتوان تعداد پارامترهای ورودی یک فانکشن را در هنگام تعریف آن مشخص کرد که برای مثال برخی فانکشنهای Built-in از جمله فانکشن ()print
را نام میبریم که بارها در زمان فراخوانی به تعداد دلخواه آرگومان ورودی به آن دادهایم.
در همین راستا، در این آموزش قصد داریم تا با فانکشنهایی با تعداد پارامترهای ورودی متغیر آشنا شویم که در هنگام فراخوانی قابلیت دریافت تعداد آرگومانهای ورودی متفاوتی دارند. در واقع، تعریف فانکشنهایی با پارامترهای ورودی متغیر به دو شیوه انجام میشود که بدین طریق در روش اول میتوانیم فانکشن مد نظر را با پاس دادن یکسری آرگومان ورودی دلخواه و با رعایت ترتیب مد نظر در تعریف فانکشن فراخوانی کنیم و در روش دوم امکان فراخوانی فانکشن با انتساب آرگومان ورودی مد نظر به پارامتر ورودی متناظرش را داریم که در این روش رعایت ترتیب آرگومانهای ورودی ضرورتی ندارد چرا که به طور مشخص آرگومان ورودی مد نظر خود را به پارامتر ورودی متناظرش اختصاص داده و فانکشن مذکور را فراخوانی مینماییم که در ادامۀ آموزش به بررسی نحوۀ پیادهسازی فانکشنهایی با قابلیتهای فوقالذکر پرداخته و کاربردهای هر یک را بررسی میکنیم.
پیش از ادامۀ بحث، به بررسی تفاوت مابین هر یک از مفاهیم پارامتر و آرگومان در فانکشنها میپردازیم. به طور کلی، متغیرهایی که به عنوان ورودی در هنگام تعریف یک فانکشن در نظر گرفته میشوند، پارامتر ورودیِ فانکشن مذکور نامیده شده و دیتایی که در هنگام فراخوانی یک فانکشن به عنوان ورودی به آن پاس داده میشود تحت عنوان آرگومان ورودی شناخته میشود. برای درک بهتر تفاوت این دو مفهوم، مثال زیر را مد نظر قرار میدهیم:
>>> def multiply(a, b):
print (a * b)
>>> multiply(2, 3)
6
در کد فوق، یک فانکشن ساده تحت عنوان ()multiply
تعریف کرده و دو متغیر ورودی به نامهای a
و b
برای آن در نظر گرفتهایم که در اینجا هر یک از متغیرهای a
و b
به منزلۀ پارامتر ورودی به فانکشن مذکور میباشند. در ادامه، دستورات داخلی فانکشن را داریم که گفتهایم هر یک از مقادیر ورودی به فانکشن ()multiply
و منتسب به پارامترهای ورودی را در هم ضرب کرده و در خروجی چاپ کند. در سطر سوم هم فانکشن ()multiply
را فراخوانی نمودهایم که در اینجا هر یک از اعداد 2 و 3 به عنوان آرگومان ورودی در نظر گرفته شده و به ترتیب به پارامترهای ورودی a
و b
منتسب شده و در هم ضرب میشوند و در نهایت هم عدد 6 در خروجی چاپ میگردد (توجه داشته باشیم که در فراخوانی یک فانکشن تعداد آرگومانهای ورودی میباید دقیقاً مشابه تعداد پارامترهای ورودی تعریفشده برای آن باشند.)
تعریف فانکشنی با پارامترهای ورودی متغیر با بهکارگیری *
در زبان پایتون قابلیتی تعبیه شده است که با بهکارگیری آن میتوانیم تعداد آرگومانهای متغیری را به یک فانکشن پاس دهیم که برای این کار کافی است تا در حین تعریف فانکشن و در کنار پارامتر ورودی آن کاراکتر *
را قرار دهیم. برای مثال، در برنامۀ زیر فانکشنی تعریف کردهایم که تعداد پارامترهای ورودی آن متغیر است و بدین ترتیب قادر خواهیم بود تا فانکشن مذکور را با تعداد متفاوتی از آرگومانهای ورودی فراخوانی کنیم:
def varArgFunc(*args):
print(*args)
varArgFunc() # 0 argument
varArgFunc('Sokan', 'Academy') # 2 arguments
varArgFunc(1, 2, 3, 4, 5) # 5 arguments
varArgFunc(9, 'Nine') # 2 arguments
varArgFunc ('\n', '***', '\n', '* *', '\n', '***' , '\n') # 7 arguments
در کد فوق، فانکشنی تحت عنوان ()varArgFunc
تعریف کرده و یک پارامتر ورودی به نام دلخواهِ args
برای آن در نظر گرفتهایم که با قرار دادن کاراکتر *
در کنار این پارامتر گفتهایم که فراخوانی فانکشن مذکور با دریافت تعداد آرگومانهای ورودی دلخواه امکانپذیر میباشد و در ادامه این فانکشن را با یکسری آرگومان متفاوت فراخوانی کردهایم سپس اسکریپت برنامۀ فوق را در فایلی به نام varArgFunc.py
ذخیره کرده و آن را اجرا میکنیم که در نهایت خروجی حاصل به صورت زیر خواهد بود:
========== RESTART: D:/SokanAcademy/Python/varArgFunc.py ==========
Sokan Academy
1 2 3 4 5
9 Nine
***
* *
***
همانطور که میبینید، در ابتدا فانکشن ()varArgFunc
را بدون آرگومان ورودی فراخوانی کردهایم که منجر به چاپ یک سطر خالی در خروجی شده است و در ادامه دو آرگومان ورودی از نوع استرینگ به فانکشن مذکور داده و آن را فراخوانی کردهایم که فانکشن ()print
هر یک از آنها را گرفته و به ترتیب با ایجاد یک فاصله از هم چاپ میکند. سایر دستورات فراخوانی نیز به همین صورت عمل میکنند و در هر فراخوانی نیز آرگومانهای ورودی به فانکشن به ترتیب به پارامتر args
ارجاع داده شده و در خروجی چاپ میشوند به طوری که هر یک از آرگومانهای ورودی به ترتیب به پارامترهای فانکشن منتسب شده و در ادامه با یک فاصله از آرگومان ورودی قبل چاپ میشوند.
در دستور فراخوانی چهارم یکی از آرگومانهای ورودی از نوع عدد صحیح و دیگری از نوع استرینگ است و از همین روی میبینیم که آرگومانهای ورودی به چنین فانکشنهایی میتوانند از یک نوع یا کلاس متفاوت باشند. حال برنامۀ مربوط به مثال قبل را اندکی تغییر داده و فانکشنی تحت عنوان ()varArgFunc1
را به صورت زیر پیادهسازی میکنیم:
def varArgFunc1(*args):
print(*args)
print(args)
print(len(args))
varArgFunc1(1, 2, 3)
در کد فوق، فانکشنی به نام ()varArgFunc1
تعریف کردهایم که با ذکر کاراکتر *
در کنار پارامتر ورودی آن میتوانیم فانکشن مذکور را با تعداد آرگومانهای ورودی متغیر فراخوانی کنیم و در داخل این فانکشن گفتهایم که در صورت فراخوانی، ابتدا آرگومانهای ورودی به ترتیب و با یک فاصله از هم چاپ شوند و در ادامه مشابه دستور اول عمل کردهایم با این تفاوت که کاراکتر *
را از ابتدای شناسۀ پارامتر ورودی حذف کردهایم که منجر بدین خواهد شد تا متغیر args
به آبجکتی از نوع tuple منتسب شده است که در آموزش آشنایی با دیتا تایپ تاپل در زبان برنامهنویسی پایتون به بررسی آن پرداختهایم اما در حال حاضر کافی است بدانیم که با حذف *
از ابتدای شناسۀ پارامتر میتوانیم به این آبجکت دسترسی پیدا کنیم که دستور ()print
نیز منجر به چاپ آبجکت مذکور در خروجی خواهد شد.
در سطر بعد از بدنۀ فانکشن هم خروجی فانکشن ()len
را به عنوان آرگومان ورودی به فانکشن ()print
دادهایم که بدین ترتیب ()len
تعداد آرگومانهای ورودی به فانکشن ()varArgFunc1
را محاسبه کرده و خروجی را برای چاپ به فانکشن ()print
میدهد و در نهایت فانکشن مذکور را با سه پارامتر ورودی فوق فراخوانی کردهایم که خروجی حاصل از این فراخوانی به صورت زیر خواهد بود:
1 2 3
(1, 2, 3)
3
همانطور که میبینید، در ابتدا هر یک از آرگومانهای ورودی با یک فاصله از هم چاپ شدند و در ادامه آرگومانهای مذکور در قالب یک آبجکت از نوع تاپل در معرض دیدمان قرار میگیرد و دستور آخر از فانکشن ()varArgFunc1
منجر به چاپ تعداد آرگومانهای ورودی میگردد. به منظور درک بهتر این موضوع، به عنوان مثالی دیگر داریم:
def addition(*args):
sum = 0
for num in args:
sum += num
print(sum)
addition(1, 2, 3, 6)
در کد فوق فانکشنی تحت عنوان ()addition
با پارامتر ورودی args
تعریف کردهایم که قرار دادن علامت *
در کنار پارامتر ورودی منجر بدین میشود تا بتوانیم در هنگام فراخوانی فانکشن مذکور به تعداد دلخواه آرگومان ورودی به فانکشن مد نظر پاس دهیم و در ادامه داخل فانکشن ()addition
ابتدا متغیری تحت عنوان sum
با مقدار اولیۀ 0 تعریف کردهایم که قرار است تا مقدار حاصل از جمع آیتمهای مربوط به آرگومان ورودی را نگهداری کند سپس یک حلقۀ for
تعریف کردهایم که در آن گفتهایم هر یک از آیتمهای مربوط به آرگومان ورودی را به ترتیب به متغیری تحت عنوان num
منتسب کرده و آنها را با هم جمع کرده و حاصل را در متغیر sum
ذخيره كند و در نهايت مقدار منتسب به متغير sum
را در خروجی چاپ کند. حال کد فوق را در فایلی به نام additionArgs.py
ذخیره کرده و آن را اجرا میکنیم که نتیجۀ حاصل از فراخوانی فانکشن فوق با تعداد 4 آرگومان ورودی به صورت زیر میباشد:
12
اکنون تعداد پارامترهای ورودی فانکشن فوق را به صورت زیر به عدد 3 کاهش میدهیم:
def addition(*args):
sum = 0
for num in args:
sum += num
print(sum)
addition(1, 2, 3)
با اجرای کد فوق، فانکشن ()addition
این بار با 3 آرگومان ورودی فراخوانی شده و در خروجی خواهیم داشت:
6
همانطور که میبینیم، با قرار دادن علامت *
در ابتدای شناسۀ پارامتر ورودیِ فانکشنها موجب میشود تا بتوانیم فانکشن مد نظر را با تعداد دلخواهی از آرگومانهای ورودی فراخوانی نماییم به طوری که هر یک از آرگومانهای ورودی به ترتیب به پارامتر ورودی تعریفشده برای فانکشن منتسب شده و دستورات مد نظر روی هر یک اِعمال میگردد.
حال فرض میکنیم فانکشنی همچون ()addition
در این مثال را به روشی رایج و به صورت زیر تعریف کردهایم:
def addition(a, b, c):
sum = 0
sum = a + b + c
print(sum)
در واقع، در کد فوق فانکشنی تحت عنوان ()addition
با ۳ پارامتر ورودی به نامهای b
،a
و c
تعریف کردهایم و داخل فانکشن نیز متغیری به نام sum
با مقدار اولیۀ 0 تعریف کرده و گفتهایم مقدار حاصل از جمع مقادیر منتسب به پارامترهای ورودی در این متغیر نگهداری شود و در نهایت مقدار ذخیرهشده در متغیر مذکور توسط فانکشن ()print
در خروجی چاپ گردد که اگر بخواهیم فانکشن مذکور را بر اساس آنچه که در آموزشهای پیشین آموختیم فراخوانی کنیم، به صورت زیر عمل میکنیم:
addition(1, 2, 3)
در نتیجۀ فراخوانی فانکشن فوق، هر یک از آرگومانهای ورودی با هم جمع شده و در خروجی خواهیم داشت:
6
به علاوه، اگر بخواهیم هر یک از آرگومانهای ورودی را در قالب آبجکتی از جنس لیست یا تاپل به فانکشن مد نظر پاس داده و آن را فراخوانی کنیم، کدی به صورت زیر خواهیم داشت:
arguments = {1, 2, 3}
addition(*arguments)
همانطور که ملاحظه میکنیم، آبجکتی از جنس لیست متشکل از ۳ عدد فوق تعریف کرده را به آن اختصاص دادهایم و در ادامه با بهکارگیری علامت *
شناسۀ لیست مذکور را به عنوان آرگومان ورودی به فانکشن ()addition
دادهایم که با فراخوانی فانکشن مذکور هر یک از اِلِمانهای لیست مورد نظر به ترتیب به پارامترهای تعریفشدۀ فانکشن منتسب شده و دستورات داخلی فانکشن مذکور روی آنها اِعمال میگردد.
توجه داشته باشیم که تعداد اِلِمانهای آرگومان ورودی حتماً میباید با تعداد پارامترهای ورودی تعریفشده برای فانکشن برابر باشند (جهت آشنایی با نحوۀ تعریف آبجکتهایی از جنس لیست به آموزش آشنایی با دیتا تایپ لیست در زبان برنامهنویسی پایتون مراجعه نمایید.) اما در ادامۀ این آموزش به تشریح روش دوم از تعریف فانکشنهایی با پارامترهای ورودی متغیر میپردازیم.
تعریف فانکشنی با پارامترهای ورودی متغیر با بهکارگیری **
در روش دوم از تعریف فانکشنهایی با پارامترهای ورودی متغیر نامی دلخواه همچون kwargs
برای پارامتر ورودی در نظر گرفته و از علامت **
در کنار شناسۀ آن استفاده میکنیم و بدین طریق در هنگام فراخوانی فانکشن مذکور میتوانیم به طور مشخص هر یک از آرگومانهای ورودی مورد نظر را به پارامترهای ورودی دلخواه منتسب نماییم (توجه داشته باشیم که kwargs مخفف واژگان Keyworded Arguments بوده و به فراخوانی فانکشن با آرگومانهای ورودی منتسب به پارامتر ورودی متناظرشان اشاره دارد.)
در واقع، در فراخوانی فانکشنها بدین روش هر یک از آرگومانهای ورودی به همراه متغیر متناظرشان در قالب آبجکتی از جنس دیکشنری به فانکشن مذکور پاس داده میشوند به طوری که متغیرهای مد نظر به عنوان مقادیر Key و آرگومانهای ورودی منتسب به آنها نیز به عنوان Value در آبجکت دیکشنری مذکور به شمار میروند (در آموزش آشنایی با دیتا تایپ دیکشنری در زبان برنامهنویسی پایتون به طور مفصل به معرفی آبجکتهایی از جنس دیکشنری پرداختهایم.) حال برای درک بهتر نحوۀ پیادهسازی این فانکشن، کد مربوط به مثال قبل را به صورت زیر بازنویسی میکنیم:
def addition(**kwargs):
result = 0
for i in kwargs:
result += kwargs[i]
print(result)
addition(varA = 1, varB = 2, varC = 3, varD = 6)
در تفسیر کد فوق باید گفت که فانکشنی تحت عنوان ()addition
با پارامتر ورودی دلخواهی به نام kwargs
تعریف کردهایم که با قرار دادن علامتهای **
در ابتدای نام پارامتر ورودی، قابلیت فراخوانی فانکشن مذکور با تعداد پارامترهای ورودی متغیر و در قالب آبجکتی از جنس دیکشنری را بدان دادهایم. در ادامه، متغیری با مقدار اولیۀ 0 تحت عنوان result
تعریف نمودهایم که قرار است تا نتیجۀ حاصل از مقادیر Value آبجکت دیکشنری مد نظر را در خود نگهداری کند سپس یک حلقۀ for
تعریف کردهایم تا به ازای هر یک از مقادیر آرگومان ورودی، مقدار متناظرِ هر یک از متغیرها یا مقادیر Value در آبجکت دیکشنریِ ورودی به فانکشن را به متغیر i
منتسب کرده و در ادامه هر یک از مقادیر منتسب به متغیر مذکور با یکدیگر جمع شده و به متغیر result
منتسب شوند و در نهایت هم مقدار ذخیرهشده در متغیر result
توسط فانکشن ()print
در خروجی چاپ شود که برای درک بهتر این موضوع، در سطر بعد فانکشن مذکور را با انتساب هر یک از آرگومانهای ورودی به پارامتر ورودی متناظرشان فراخوانی کردهایم.
حال کد فوق را در فایلی به نام additionKwargs.py
ذخیره کرده و آن را اجرا مینماییم. در واقع، با فراخوانی فانکشن ()addition
در مثال فوق، هر یک از متغیرهای varB
،varA
و سایر متغیرها به عنوان پارامتر ورودی و مقدار منتسب به هر یک از متغیرها نیز به عنوان آرگومان ورودی فانکشن ()addition
در نظر گرفته شده و فانکشن مذکور فراخوانی میشود که نتیجۀ حاصل از این فراخوانی به صورت زیر خواهد بود:
12
در این مرحله، مشابه روش اول قصد داریم تا با بهکارگیری علامتهای **
فانکشنی را فراخوانی کنیم که با تعداد مشخصی پارامتر ورودی تعریف شده است که برای این منظور فانکشن مربوط به مثال قبل را مجدداً مبنا قرار میدهیم:
def addition(a, b, c):
sum = 0
sum = a + b + c
print(sum)
همانطور که میبینیم، فانکشنی تحت عنوان ()addition
با ۳ پارامتر ورودی تعریف کردهایم که قصد داریم تا آبجکتی از جنس دیکشنری را به عنوان آرگومان ورودی به این فانکشن پاس داده و آن را فراخوانی نماییم که برای این منظور به صورت زیر عمل میکنیم:
myDictionary = {"b":"2", "a":"1", "c":"3"}
addition(**myDictionary)
در کد فوق، آبجکتی از جنس دیکشنری با مقادیر Key معادل استرینگهای «b» ،«a» و «c» و مقادیر Value متناظرشان معادل با 1، 2 و 3 تعریف کردهایم و آن را به متغیری به نام myDictionary
منتسب نمودهایم که با بهکارگیری علائم **
میتوانیم این آبجکت را به عنوان آرگومان ورودی به فانکشن ()addition
پاس دهیم. در حقیقت، با فراخوانی این فانکشن هر یک از مقادیر Key در دیکشنری مد نظر به عنوان پارامتر ورودی و مقادیر Value متناظرشان به عنوان آرگومان ورودیِ منتسب به این فانکشن در نظر گرفته میشوند.
نکته |
موضوع قابلتوجه در مثال فوق این است که تعداد اِلِمانهای دیکشنری مد نظر حتماً میباید با تعداد پارامترهای ورودی تعریفشده برای فانکشن برابر باشند و مقادیر Key تعریفشده برای دیکشنری باید دقیقاً همنام با پارامترهای تعریفشده برای فانکشن در نظر گرفته شوند به علاوه اینکه رعایت ترتیب در اختصاص آرگومان ورودی به پارامترهای فانکشن مد نظر ضرورتی ندارد چرا که به طور مشخص هر یک را به پارامتر ورودی متناظرش اختصاص میدهیم. |
حال با توجه به اینکه هر روز اجناس متنوعی در فروشگاه فروخته میشوند و مثلاً ممکن است در یک روز ۱۰۰ قلم جنس متفاوت در فروشگاه موجود باشد اما هفتۀ بعد این تعداد به نصف کاهش پیدا کند، در این صورت اگر اپلیکیشن مذکور با زبان برنامهنویسی پایتون پیادهسازی شود امکان استفاده از فانکشنهایی با تعداد آرگومانهای ورودی منجر به تسهیل توسعۀ آن خواهد شد.