در توسعه هر برنامه ای حتما قطعه کدهایی وجود دارند که چندین و چند مرتبه درون برنامه مورد استفاده قرار می گیرند. تکرار مکرر و نوشتن این قطعه کدها در بخش های مختلف برنامه قاعدتا باعث سردرگمی برنامه نویس و ناخوانایی کدهای برنامه می شود. بنابراین به جای تکرار این قطعه کدها می توان از تعریف توابع استفاده کرد. با تعریف تابع می توان مجموعه ای از کدهای مرتبط به هم که هدفی خاص را انجام می دهند را درون یک بلوک جمع آوری و نامگذاری کرد و سپس توسط نام تابع، می توان قطعه کدهای درون بدنه یا بلوک تابع را اجرا کرد.

در پایتون توابع می توانند صفر تا چندین آرگومان ورودی داشته باشند که این آرگومان ها می توانند از انواع رایج پایتون یا نمونه کلاس های تعریف شده توسط کاربران باشند. برخلاف توابع در زبان هایی مانند C که تابع می تواند دارای نوع بازگشتی باشد، در پایتون تعیین نوع بازگشتی وجود ندارد. برای تعریف تابع در پایتون از کلمه کلیدی def به همراه نام تابع استفاده می شود.فرمت کلی توابع به صورت های زیر است. در حالت اول تابع هیچ آرگومانی ورودی دریافت نمی کند و در حالت دوم تابع تا چند آرگومان ورودی را دریافت کرده است که با ویرگول از هم جدا شده اند.

همانطور که مشخص است همانند ساختارهای شرطی و کنترلی، بدنه توابع نیز توسط فاصله گذاری از سمت چپ مشخص می شوند. در تعریف توابع پس از کلمه کلیدی def نام تابع می آید و به دنبال آن در پرانتزهای باز و بسته قرار می گیرند. درون پرانتزهای باز و بسته می توان صفر تا چندین آرگومان ورودی را تعیین کرد. در نهایت تعریف تابع با علامت : تمام می شود و سپس در خط بعدی با یک فاصله از سمت چپ بدنه تابع شروع می شود تا اینکه به دستور return برسیم.

در تعریف پایتون استفاده از کلمه کلیدی و نوشتن نام تابع به همراه پرانتزهای باز و بسته و در انتها علامت : الزامی است ولی تعیین پارامترها و بازگشت دادن مقدار توسط return الزامی نیست. درون بدنه از یک تا چند عبارت تشکیل شده است که پردازش و محاسبات را انجام می دهند. اگر تابع آرگومان ورودی دریافت کند، دستورهای درون بدنه تابع بر روی آن انجام می شوند و پس از انجام پردازش و محاسبات، خروجی تولید می شود. توابع در پایتون به دو دسته توابع درونی (Built-in Functions) و توابع تعریف شده کاربران (User Defined Functions) تقسیم می شوند. کلاس ها نیز توابع عضو خود را دارند که متد (Method) گفته می شوند. هر نمونه از کلاس می تواند به متدهای درون کلاس دسترسی خواهند داشت.

مثال ۱ – در قطعه کد ۱ تابع sayHello هیچ آرگومان ورودی را دریافت نمی کند و درون بدنه خود تنها یک دستور دارد. عملکرد تابع به این صورت است که تنها پیغام Hello World را چاپ می کند.

مثال ۲ – تابعی بنویسید که دو عدد از ورودی بگیرد و حاصل جمع این دو عدد را نشان دهد.

فراخوانی توابع

برای استفاده از توابع (تعریف شده توسط کاربر، توابع درونی و یا متدهای کلاس) باید توسط نام توابع و ارسال مقادیر به آرگومان های ورودی، توابع را فراخوانی کرد. در صورتی که تابع از چند آرگومان ورودی تشکیل شده باشد، پس باید به ترتیب تعریف آرگومان، مقادیر را به تابع ارسال کرد. بنابراین برای استفاده از دو تابع در مثال های ۱ و ۲ باید مطابق با قطعه کد ۳ توابع را فراخوانی کرد. تابع ()sayHello هیچ آرگومان ورودی ندارد ولی تابع summation دو آرگومان ورودی را لازم دارد. آرگومان های ورودی برای تابع ()summation از نوع اعداد صحیح یا اعشاری هستند.

مثال ۳ – تابعی بنویسید که یک لیست را از وروی دریافت کند و تمامی اعداد درون لیست را با هم جمع کند و نتیجه را نمایش دهد.

برای استفاده باید یک لیست یا متغیری از نوع لیست را به عنوان ورودی به تابع ()list_summation ارسال کنیم. (فرض شده که لیست ورودی به صورت تو در تو نیست). بنابراین اگر تابع را با لیست [1, ‘a’, 2.3, ‘b’, 1] فراخوانی کنیم نتیجه عدد 4.300000 خواهد بود. ابتدا یک متغیر با مقدار اولیه صفر برای محاسبه و نگهداری نتیجه تعریف شده است. در مفاهیم برنامه نویسی چون این متغیر درون بدنه یا بلوک تابع تعریف شده است، پس این متغیر از نوع محلی (Local Variable) برای این تابع است.

در خط چهارم از تابع ()isinstance استفاده شده است تا بررسی شود که ‌آیا عنصر فعلی از لیست ورودی از نوع اعداد صحیح یا اعشاری باشد. اگر عدد بود پس با مقدار قبلی درون متغیر result جمع می شود و نتیجه مجدد در خود متغیر result ذخیره می شود. تابع ()isinstance یک تابع درونی و از پیش تعریف شده در پایتون است. به عنوان تمرین قطعه کد مثال ۳ را برای لیست های تو در تو باز نویسی کنید.

مقدار بازگشتی

در تمامی قطعه کدهای بالا توابع مقداری را برگشت نمی دادند بلکه پس از محاسبات نتیجه را چاپ می کنند. درون توابع می توان از return برای برگشت دادن خروجی استفاده شود. از مقدار برگشتی می توان در حلقه شرطی استفاده کرد و یا اینکه مقدار برگشتی را درون یک متغیر ذخیره کرد.

مثال ۴ – مثال ۳ را به گونه ای باز نویسی می کنیم که به جای چاپ خروجی مقدار آنرا برگشت دهد.

بنابراین می توان از مقدار برگشتی توسط تابع درون حلقه شرطی استفاده کرد. قطعه کد ۶ مثالی را نشان می دهد که اگر مقدار برگشتی بزرگتر از ۵ باشد پیغام Great than و در غیر این صورت پیغام Less than چاپ می شود.

مثال ۵ – تابعی بنویسید که دو ارگومان دریافت کند. آرگومان اول به دیکشنری و دیگری  به کلیدی درون دیکشنری اشاره می کند. اگر کلید ارسالی درون کلیدهای دیکشنری وجود داشت، مقدار آن کلید برگشت داده می شود و در غیر این صورت مقدار False برگشت داده می شود. از مثال ۵ مشخص می شود که توابع می توانند بر حسب عملکرد و وظیفه خود و نسبت به شروط، مقدار متفاوتی را برگشت دهند.

مثال ۶ – تابع مثال ۵ را باز نویسی کنید که تابع سه آرگومان دریافت کند. آرگومان اول لیستی از دیکشنری ها باشد و آرگومان های دوم کلید و مقدار منتسب به آن باشد. اگر کلید درون هر یک از دیکشنری ها نبود، کلید و مقدار ارسالی به آن به دیکشنری اضافه شود.

 در خط ۸ sample1 لیستی از دیکشنری ها است که از دو دیکشنری تشکیل شده است. کلید های name, age و locaton در دیکشنری اول وجود دارند و لی کلید location در دیکشنری دوم وجود ندارد، پس با فراخوانی تابع ()set_value می خواهیم جفت کلید و مقدار location:Tehran را به دیکشنری دوم اضافه کنیم. نتیجه اجرای تابع در متغیر sample2 ذخیره می شود.

آرگومان ها با مقدار پیشفرض

در توابع آرگومان ها می توانند مقادیر پیشفرض داشته باشند. برای انتساب مقدار پیشفرض به یک آرگومان از علامت انتساب (=) استفاده می شود. در صورتی که در زمان فراخوانی تابع به صورت متستقیم به آرگومان مفداری انتساب داده نشود، آرگومان با مقدار پیشفرض مورد استفاده قرار می گیرد.

مثال ۷ – تابعی بنویسید که یک نام و پیغام را دریافت کند و سپس در خروجی نشان دهد. آرگومان msg دارای مقدار پیشفرض Salam است.

در قطعه کد ۹ یکبار تابع فقط با مقداردهی آرگومان اول فراخوانی شده و مرتبه دوم هر دو آرگومان مقداردهی شده اند. اما برای انتساب مقدار پیشفرض به آرگومان ها یک قاعده وجود دارد و آن اینکه باید تمامی آرگومان هایی که می خواهیم دارای مقدار پیشفرض باشند باید در انتهای لیست آرگومان ها قرار گیرند. بنابراین قطعه کد ۱۰ منجر به خطای SyntaxError: non-default argument follows default argument می شود.

قاعده کلی به این صورت است که اگر اولین آرگومان دارای مقدار پیشفرض بود، بنابراین بقیه آرگومان ها نیز باید دارای مقدار پیشفرض باشند. همچنین اگر از میان هر چند آرگومان تابع، جایی آرگومانی با مقدار پیشفرض تعیین شد، حتما باید بقیه آرگومان های پس از آن نیز دارای مقدار پیشفرض باشند. اگر فرض کنیم تابع دارای ۵ آرگومان باشد، قطعه کد ۱۱ دو آرگومان اول را بدون مقدار پیشفرض و سه تای آخری (از آرگومان سوم به آخر) را با مقدار پیشفرض تعیین کرده است.

ترتیب ارسال آرگومان ها به تابع

در قطعه کد ۱۲ یک تابع به سه آرگومان تعریف شده است. هر سه آرگومان از نوع رشته هستند. در زمان فراخوانی از سمت چپ به راست آرگومان ها ارسال و خروجی نمایش داده می شود.

در خطوط ۵ و ۸ مقادیر متفاوتی به تابع ارسال شده اند و خروجی آخرین فراخوانی (خط ۸) قاعدتا خروجی مطلوب نیست. د ر قطعه کد ۱۱ به دلیل اینکه هدف ارسال رشته بوده است، مشکل جدی پیش نیامده است و این مورد زمانی مشکل ساز خواهد شد که تابع پیچیده باشد و ترتیب ارسال آرگومان اهمیت دارد چون مقادیر ارسالی برای پردازش و محاسبات درون تابع استفاده می شوند.

بنابراین یا خروجی نادرست ایجاد می شد یا به طور کل خطا نشان داده می شود. برای رفع این مشکل بهتر است که مانند قطعه کد ۱۳ نام آرگومان ها را به همراه مقادیر منتسب به آنها را با هم در زمان فراخوانی تابع بیاوریم. در این صورت می توان ترتیب ارسال آرگومان ها را نیز در نظر نگرفت، چون به طور صریح در زمان فراخوانی تابع، آرگومان و مقدار منتسب به آنرا مشخص کرده ایم.

آرگومان های دلخواه

به طور معمول هر تابع تعداد مشخص و معنی آرگومان را دریافت می کند. اما گاهی اوقات از پیش تعداد آرگومان های ارسالی به تابع را نمی دانیم. برای چنین مواردی می توان از آرگومان های دلخواه (Arbitrary Argument) برای ارسال چندین آرگومان به تابع استفاده کرد. در تعریف آرگومان های Arbitrary از یک ستاره * () در ابتدای نام آرگومان استفاده می شود. در واقع با استفاده از این آرگومان ها می توان در زمان فراخوانی تابع، چندین مقداردلخواه  را به آن ارسال کرد. در صورتی که به آرگومان های معمولی فقط یک مقدار ارسال می شود. تفاوت این آرگومان ها با آرگومان های معمولی در این است که آرگومان های Arbitrary اختیاری (Optional) هستند.

در قطعه کد ۱۴ یک آرگومان از نوع Arbitrary تعریف کرده ایم و در زمان فراخوانی تابع، چندین مقدار را به تابع ()say_hello ارسال کرده ایم. در نهایت خروجی تابع شامل چند خط به ازای هر مقدار ارسال شده به تابع است. همچنین می توان در کنار آرگومان Arbitrary آرگومان های معمولی را داشته باشیم. قطعه کد ۱۵ بازنویسی شده قطعه کد ۱۴ است که در آن در آرگومان اول به صورت معمولی و آرگومان سوم از نوع آرگومان Arbitrary است.

در فراخوانی تابع ()say_hello در قطعه کد ۱۵ دو مقدار اول برای آرگومان های first و second و دو مقدار آخر برای آرگومان third هستند. همانطور که گفته شد آرگومان های Arbitrary اختیاری هستند پس می توان مقداری را به آنها ارسال نکرد. قطعه کد ۱۶ نمونه دیگری از فراخوانی تابع ()say_hello را نشان می دهد.

به دلیل اینکه آرگومان های Arbitrary به صورت اختیاری هستند، بنابراین باید در آخر فهرست تعریف آرگومان ها قرار گیرند در غیر این صورت خطا نشان داده می شود.

مثال ۸ – تابعی بنویسید که تعداد دلخواهی دیکشنری دریافت کند. هر دیکشنری دارای کلید های name و age است. همچنین کلید location در برخی وجود ندارد. از طریق این تابع بررسی کنید در کدام دیکشنری ها کلید location وجود ندارد. سپس مقدار کلید name هر یک از این دیکشنری ها را به یک لیست اضافه کنید و در نهایت لیست را برگشت دهید.

همانطور که گفته شد آرگومان های Arbitrary اختیاری هستند و می توان مقداری را برای آنها به تابع ارسال نکرد. در قطعه کد ۱۹ را بدون مقدار فراخوانی کنیم، مشکلی پیش نمی آید و خروجی یک لیست خالی خواهد بود. چون ورودی آرگومان های Arbitrary یک لیست است، می توانیم تعداد مقادیر ارسال شده را توسط تابع ()len پیدا کنیم.

متغیرهای محلی و سراسری

در زبان های برنامه نویسی دو نوع متغیر محلی (Local Variable) و متغیر سراسری (Global Variable) وجود دارند. محلی یا سراسری بودن یک متغیر، اشاره به حوزه (Scope) تعریف آن متغیر دارد. در پایتون، به طور کلی هر متغیری که درون یک تابع تعریف شود و یا متغیری درون تابع با مقدار جدید، مقدار دهی شود، یک متغیر محلی خواهد بود.

بنابراین هر متغیری که خارج از حوزه یا بدنه یک تابع تعریف شود، یک متغیر سراسری خواهد بود. تفاوت متغیرهای محلی و سراسری در این است که، یک متغیر سراسری تنها درون خود بدنه بلاک قابل دسترسی و معنا دار است، اما یک متغیر سراسری توسط تمامی توابع قابل دسترسی هستند.

قطعه کد ۲۰ یک مثال ساده را نشان می دهد که در آن متغیر string پیش از تعریف تابع ایجاد شده و درون بدنه تابع فقط یک عبارت print string داریم. در این حالت درون بدنه تابع هیچ متغیری به نام string وجود ندارد، بنابراین تابع از متغیر string خارج از بدنه خود استفاده می کند. در اینجا string به صورت سراسری توسط تمامی توابع قابل دسترس است.

حال فرض کنید مطابق قطعه کد ۲۱ تابع ()func را پیش از متغیر سراسری string تعریف می کنیم. در این حالت نیز این متغیر برای هر دو تابع قابل دسترسی است.