در آموزشهای گذشته با مفهوم فانکشن یا تابع آشنا شدیم و دیدیم که فانکشنها در زبان برنامهنویسی پایتون به عنوان یک آبجکت در نظر گرفته میشوند که میتوان هر یک از آنها را به عنوان آرگومان ورودی به فانکشنی دیگر داد. در واقع، هر فانکشن مانند دستگاهی عمل میکند که مجموعهای از دستورالعملها را شامل میشود و در شرایطی که نیاز داریم تا از یک بلوک کد چندین مرتبه در برنامۀ خود استفاده کنیم، میتوانیم آن را در قالب یک فانکشن نوشته و با فراخوانی فانکشن مذکور، تمامی دستورات بلوک مد نظر را اجرا کنیم که این امر منجر بدین خواهد شد تا نیاز به تکرار و دوبارهنویسی کدها نداشته باشیم. به علاوه، بهکارگیری مفهوم فانکشن در برنامهنویسی موجب میشود تا در صورت نیاز به راحتی بتوانیم بلوک کد مربوطه را تغییر دهیم و بدین ترتیب تغییرات مد نظر در تمامی نقاطی اِعمال خواهند شد که فانکشن مربوطه به کار گرفته شده است و دیگر نیازی به اِعمال آنها در جایجای سورسکد نخواهیم داشت.
در زبان برنامهنویسی پایتون به منظور تعریف یک فانکشن از دستورات مرکب استفاده میکنیم و همانطور که در آموزشهای قبل اشاره کردیم، هر دستور مرکب با یک کلمۀ کلیدی آغاز میشود که این کلمۀ کلیدی def
است که بدون شک از ابتدای فعل Define به معنای «تعریف کردن» گرفته شده است. ساختار کلی برای تعریف یک فانکشن به صورت زیر میباشد:
def function_name():
بر اساس ساختار فوق، میبینیم که تعریف فانکشن با کلمۀ کلیدی def
آغاز شده و در ادامه نام فانکشن و سپس علائم ()
و در انتها هم مشابه بخش هدر در تمامی بندهای دستورهای مرکب، کاراکتر :
قرار میگیرد.
به خاطر داشته باشید |
برای تعریف برخی از فانکشنهای خاص تحت عنوان فانکشنهای بینام از کیورد lambda استفاده میشود که به دلیل پیچیدگی، در مباحث پیشرفتهتر به معرفی آنها خواهیم پرداخت |
نام فانکشن یک شناسۀ دلخواه است و برای انتخاب آن باید تمامی قواعد مربوط به انتخاب شناسهها در زبان برنامهنویسی پایتون را رعایت کنیم که جهت مرور این قواعد میتوانید به آموزش آشنایی با مفهوم متغیر در این زبان مراجعه کنید.
بر اساس استانداردهای زبان پایتون، شناسۀ مناسب برای نامگذاری فانکشنها بهتر است به دو روش انتخاب شود که در روش اول تنها از کاراکترهایی با حروف کوچک انگلیسی استفاده میشود که در صورت انتخاب نام چندبخشی، کاراکترهای هر کلمه با آندِراِسکور یا علامت _
از هم جدا میشوند و بدین ترتیب نام فانکشن فرمی به صورت function_name
خواهد داشت و در حالت دوم نیز تمامی کاراکترها از بین حروف کوچک انگلیسی انتخاب شوند با این تفاوت که در مورد نامهای چندبخشی، حرف نخست هر کلمه به غیر از کلمۀ اول از میان حروف بزرگ انتخاب میشود به طوری که فرم کلی شناسۀ فانکشنها در این مورد به صورت functionName
خواهد بود که این سبک نامگذاری در برنامهنویسی را اصطلاحاً camelCase میگویند چرا که شکل شماتیک نام فانکشن همچون کوهانِ شتر است!
البته لازم به یادآوری است که رعایت چنین استانداردهایی بایدی نبوده اما به هر حال پیروی از آنها منجر به افزایش خوانایی سورسکد میشود. به علاوه اینکه استفاده از شناسههای با معنی به عنوان نام فانکشنها نیز سبب افزایش خوانایی سورسکد شده و در همین راستا توصیه میکنیم نام فانکشن را به صورت یک فعل انتخاب کنید. به طور کلی، منظور از یک فعل، کاری است که توسط یک فاعل صورت میگیرد (به عنوان مثال، فعل دویدن را میتوان نام برد که توسط یک ورزشکار انجام میگیرد.)
در برنامهنویسی نیز وظیفۀ هر فانکشن انجام یک کار است و بنابراین بهتر است که نام انتخابی برای آن متناسب با کاری باشد که انجام میدهد. برای مثال، اگر فانکشنی داریم که قرار است تا هزینۀ سبد خرید یک فروشگاه آنلاین را محاسبه کند، نامی همچون calculateCart مناسب میباشد که از فعل Calculate به معنی «محاسبه کردن» و نام Cart به معنی «سبد خرید» تشکیل شده است و به عنوان مثالی دیگر میتوان به فانکشنی برای حرکت دادن یک شیئ گرافیکی در صفحه اشاره کرد که برای نامگذاری آن نیز فعلی همچون Move و نامی مثل moveElement مناسب به نظر میرسد.
در زبان برنامهنویسی پایتون بخش سربند (هِدِر) مربوط به تعریف فانکشن خود یک دستور قابلاجرا است بدین معنی که مفسر پایتون با رسیدن به کیورد def
شروع به ساخت فانکشنی با نام مد نظر میکند و بنابراین میتوان در تمامی نقاط سورسکد از آن استفاده کرده و به طور مثال فانکشنی را در داخل بدنۀ یک فانکشن دیگر تعریف کرد. حال اگر پس از نوشتن بخش هِدِر دستوری در بدنۀ فانکشن نوشته نشود بدین معنی است که فانکشن مذکور صرفاً به مفسر معرفی شده است و در صورت فراخوانی هیچ کار خاصی انجام نخواهد داد!
همچنین در آموزشهای گذشته آموختیم که دستورات بدنۀ هر بند از یک دستور مرکب را میتوان به دو صورت نوشت که در حالت اول تمامی دستورات در همان سطر مربوط به هِدِر نوشته شده و هر یک را با علامت ;
از هم جدا میکنیم و روش دوم نیز بدین صورت است که دستورات بدنۀ هر بند را در سطرهای جداگانه نوشته و بلوک کد مربوطه نسبت به هِدِر اصطلاحاً Identation (تورفتگی) خواهد داشت.
همچنین به خاطر داشته باشیم که بر خلاف زبانهایی همچون جاوا که در آن برای بلوکبندی کدها علائم { }
به کار گرفته میشود، در زبان برنامهنویسی پایتون برای بلوکبندی کدها از تورفتگی استفاده میشود و مفسر پایتون تنها دستوراتی را به عنوان کدهای بدنۀ فانکشن میپذیرد که نسبت به بخش سربند تورفته باشند و در صورت عدم رعایت تورفتگی، بدنۀ فانکشن ناقص خواهد بود و بدین ترتیب با فراخوانی فانکشن مذکور دستورات مورد نظر اجرا نخواهند شد (در این دورۀ آموزشی برای نوشتن بخش بدنۀ بندهای دستورات مرکب از روش دوم استفاده میکنیم.) برای مثال، فانکشنی تحت عنوان ()greet
را در ادامه تعریف و فراخوانی میکنیم:
def greet():
print("Hello,")
print("Welcome to SokanAcademy.com")
greet()
greet()
greet()
کدهای فوق را در فایلی با نام دلخواه همچون greetFunc.py
ذخیره میکنیم که در آن قصد داریم تا فانکشنی تحت عنوان ()greet
برای خوشامدگویی به کاربران سکان آکادمی پیادهسازی کنیم و همانطور که میبینید، دستورات مربوط به بدنۀ این فانکشن با یک تورفتگی از بخش هِدِر جدا شدهاند و بعد از تمام شدن دستورات بدنۀ این فانکشن مجدداً به بلوک اصلی برنامه برگشته و فانکشن مذکور را سه مرتبه پشت سر هم فراخوانی کردهایم.
پیش از این نیز با نحوۀ فراخوانی فانکشنهای Built-in همچون ()help
و ()print
آشنا شدیم و دیدیم که برای فراخوانی یک فانکشن کافی است تا نام آن را به همراه علائم ()
بنویسیم که قرار دادن این پرانتزها به منزلۀ دستور دادن به مفسر پایتون برای مراجعه به فانکشن مد نظر و اجرای دستورات بدنۀ آن میباشد. در نهایت، خروجی حاصل از اجرای کدهای فوق به صورت زیر است:
Hello,
Welcome to SokanAcademy.com
Hello,
Welcome to SokanAcademy.com
Hello,
Welcome to SokanAcademy.com
اگر میخواستیم متن فوق را بدون استفاده از فانکشن ()greet
و با دستور سادۀ ()print
چاپ کنیم میبایست کدهای داخل بدنۀ فانکشن را سه مرتبه در برنامه تکرار میکردیم اما این در حالی است که با استفاده از تعریف فانکشن مذکور و سه مرتبه فراخوانی آن توانستیم با تعداد خطوط کد کمتری این کار را انجام دهیم که مسلماً این موضوع در مورد فانکشنهایی با تعداد خطوط کد فراوان ملموستر خواهد بود و منجر به افزایش سرعت توسعۀ اپلیکیشن میگردد.
در این مرحله برای درک بهتر مفهوم ایندیتیشن در زبان برنامهنویسی پایتون، تورفتگی دستور دوم در تعریف فانکشن ()greet
مربوط به مثال قبل را از بین میبریم تا ببینیم در ادامه چه اتفاقی میافتد. در واقع، کد مثال قبل را بدین صورت تغییر میدهیم:
def greet():
print("Hello,")
print("Welcome to SokanAcademy.com")
greet()
greet()
greet()
خروجی حاصل از اجرای کد فوق به صورت زیر خواهد بود:
Welcome to SokanAcademy.com
Hello,
Hello,
Hello,
همانطور که میبینید، مفسر پایتون به ترتیب شروع به اجرای دستورات برنامه میکند که ابتدا با رسیدن به دستور def
فانکشنی با نام ()greet
میسازد که در آن گفته شده پس از فراخوانی استرینگ «,Hello» را در خروجی چاپ کند و سپس مفسر دستور ()print
دوم را یک بار اجرا میکند چرا که این دستور در بدنۀ فانکشن قرار ندارد و در نتیجۀ آن نیز استرینگ «Welcome to SokanAcademy.com» در خروجی چاپ میشود و در ادامه سطرهای بعدی اجرا شده و فانکشن ()greet
سه مرتبه فراخوانی میشود که در نهایت منجر به چاپ سه سطر از استرینگ «,Hello» خواهد شد.
نکته |
نکتهای که دولوپرها، به ویژه مبتدیها، باید به آن توجه داشته باشند این است که تعداد خطوط کد مربوط به دستورات بدنۀ یک فانکشن نباید خیلی زیاد باشد و بهتر است که هر فانکشن تنها یک وظیفۀ مشخص داشته و متناسب با وظیفهاش در کوتاهترین حد ممکن پیادهسازی شود. |
در واقع، اگر برنامهنویسی اکثر دستورات برنامۀ خود را در یک فانکشن قرار دهد، مفهوم آن را به درستی درک نکرده است چرا که هدف از تعریف اشیایی همچون فانکشنها شکستن برنامه به واحدهای کوچکتر است تا بدین ترتیب امکان کنترل کدها و تصحیح خطا در آن افزایش یابد که این امر نیز به مفهوم یکسری قوانین مربوط به برنامهنویسی شیئگرا تحت عنوان SOLID اشاره دارد که برای آشنایی با آنها توصیه میکنیم به دورۀ قوانین SOLID مراجعه نمایید.
بنابراین بهترین راه برای پیادهسازی دستورات داخل بدنۀ یک فانکشن این است که بتوانید تسک مد نظر را در قالب یک فعل بیان کرده و آن را به عنوان شناسۀ فانکشن قرار دهید که برای مثال فانکشن ()greet
را برای بیان خوشامدگویی به کاربران تعریف کردیم و مسلماً انتخاب نام و پیادهسازی یک فانکشن همیشه به این سادگی نیست و نیاز به تمرین و تکرار دارد.
همانطور که پیشتر اشاره کردیم، فانکشنها در پایتون به عنوان یک آبجکت در نظر گرفته میشوند که در زمان تعریف آنها با کیورد def
یک شناسه به عنوان نام فانکشن انتخاب کرده و به فانکشن مد نظر منتسب میکنیم و این شناسه مانند نام یک متغیر است و کیورد def
نیز در اینجا مانند عملگر =
برای انتساب فانکشن به این متغیر عمل میکند که بدین ترتیب هر متغیر دیگری را میتوان به یک فانکشن منتسب کرد. برای مثال، فرض کنید بخواهیم در برنامۀ greetFunc.py
متغیر دیگری تحت عنوان welcome
را به فانکشن ()greet
منتسب کنیم که برای این منظور از دستور زیر استفاده میکنیم:
welcome = greet
توجه داشته باشیم که در دستور فوق تنها از نام متغیرها استفاده شده و علامت پرانتزهای مورد استفاده به منظور اجرای فانکشن را از آن حذف کردهایم. اکنون متغیر welcome
به یک آبجکت از نوع فانکشن منتسب شده است و بدین ترتیب فانکشن ()greet
با نام جدید ()welcome
قابلفراخوانی بوده و خروجی حاصل از این فراخوانی به ترتیب زیر خواهد بود:
>>> welcome()
Hello,
Welcome to SokanAcademy.com
در حقیقت، با اجرای کد فوق، متغیر greet
از بین نمیرود و فانکشن ()greet
با هر دو نام نیز قابلفراخوانی است چرا که شناسههای greet
و welcome
مانند یک برچسب به این فانکشن چسبیدهاند و برای فراخوانی فانکشن مذکور مورد استفاده قرار میگیرند.
در مورد فانکشنهای Built-in نیز میتوان چنین کاری را انجام داده و مثلاً فانکشن ()print
را به یک متغیر جدید تحت عنوان write
ارجاع دهیم و بدین ترتیب از متغیر write
برای فراخوانی فانکشن مذکور استفاده کنیم که برای درک بهتر این موضوع مثال زیر را مد نظر قرار میدهیم:
>>> write = print
>>> write("This is a new name for print function.")
This is a new name for print function.
همانطور که ملاحظه میکنید، فانکشن ()print
را به متغیری تحت عنوان write
منتسب کرده و در ادامه استرینگی به عنوان آرگومان ورودی به فانکشن جدید ()write
دادهایم که استرینگ مذکور را در خروجی چاپ میکند.