1 - تعریف کردن تابع

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

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

اینجا تابعی ساده به نام greet_user() می‌بینید که یک پیام احوال‌پرسی را چاپ می‌کند:

1
2
3
4
5
def greet_user():
    """Display a simple greeting."""
    print("Hello!")

greet_user()

این مثال ساده‌ترین ساختار یک تابع 68 را نشان می‌دهد. خط ۱ از کلیدواژه‌ی def استفاده می‌کند تا پایتون را مطّلع کند که شما می‌خواهید یک تابع تعریف کنید. این تعریف تابع است که به پایتون می‌گوید نام آن چیست و برای به کار بردن با آن به چه اطّلاعاتی نیاز است. پرانتزها این اطّلاعات را نگه می‌دارند. در این مورد نام تابع greet_user() است و چون برای انجام وظیفه‌اش به هیچ دانسته‌ای نیاز ندارد، پرانتزهای آن خالی مانده است. (با این حال حتّی در این حالت نیز وجود پرانتزها الزامی است) سرانجام تعریف با یک دونقطه پایان می‌یابد.

هر خطّ تورفته‌ای که پس از def greet_user(): بیاید بدنه‌ی تابع را تشکیل می‌دهد. متنِ ۲ نوعی توضیح است که docstring نامیده می‌شود و عملکرد تابع را شرح می‌دهد. Docstringها در علامت‌های نقل قول سه‌تایی قرار می‌گیرند و پایتون هنگام تولید مستندات برای تابع‌های موجود در برنامه‌ی شما به دنبال آن‌ها می‌گردد.

خط print("Hello!") در ۳ تنها خطِ برنامه در بدنه‌ی این تابع است، بنابراین greet_user() تنها یک وظیفه دارد: print("Hello").

زمانی که می‌خواهید از این تابع استفاده کنید، آن را فراخوانی می‌کنید. فراخوانی تابع از پایتون می‌خواهد که کد داخل آن را اجرا کند. برای فراخوانی یک تابع نام آن را می‌نویسید و پس از آن اطّلاعات ضروری را در پرانتز بعد از آن می‌آورید، چنان‌که در ۴ نشان داده شده است. چون اینجا به هیچ اطّلاعاتی نیاز نیست، فراخوانی تابع ما به سادگی و تنها با وارد کردن greet_user() انجام می‌پذیرد. همان‌طور که انتظار داشتیم، این تابع Hello! را چاپ می‌کند:

1
Hello!

2 - انتقال اطّلاعات به یک تابع

با کمی تغییر، تابعِ greet_user() نه تنها به کاربر سلام می‌کند، بلکه می‌تواند نام او را هم ذکر کند. برای این که تابع چنین کند username را در پرانتزهای تعریفِ تابع در def greet_user() می‌نویسید. با افزودن username به آن‌جا، تابع را قادر می‌سازید که هر مقدار usernameای که تعیین می‌کنید، بپذیرد. اکنون پایتون انتظار دارد که در هر بار فراخوانیِ تابع، مقداری برای username ارائه دهید. هنگامی که greet_user() را فرامی‌خوانید می‌توانید در پرانتزهای آن، نامی مانند 'jesse' را به این تابع بدهید:

1
2
3
4
5
def greet_user(username):
    """Display a simple greeting."""
    print(f"Hello, {username.title()}!")

greet_user('jesse')

وارد کردن greet_user('jesse') تابع greet_user() را فراخوانی کرده و به آن اطّلاعات مورد نیاز را برای اجرا کردن تابع print() ارائه می‌دهد. این تابع نامی را که به آن داده‌اید گرفته و پیامی خطاب به او نمایش می‌دهد:

1
Hello, Jesse!

مشابه این، وارد کردن greet_user('sarah') تابع greet_user() را فراخوانده، 'sarah' را به آن منتقل کرده و Hello, Sarah! را چاپ می‌کند. می‌توانید greet_user() را هر زمان که بخواهید فرابخوانید و هر نامی را که خواستید به آن بدهید تا هر بار یک خروجی قابل پیش‌بینی تولید کند.

پارامترها و نشان‌وندها

تابع greet_user() قبلی را طوری تعریف کردیم که مقداری برای متغیّر username درخواست کند. با فراخوانی آن و ارائه‌ی اطّلاعاتی که نیاز دارد (نام فرد)، این تابع پیام مناسب را چاپ می‌کرد.

متغیّر username در تعریفِ تابع greet_user() نمونه‌ای از یک پارامتر 70 است، اطّلاعاتی که تابع برای انجام کار خود نیاز دارد. مقدار 'jesse' در greet_user('jesse') نمونه‌ای از نشان‌وند یا آرگومان است. نشان‌ون 71 د اطّلاعاتی است که هنگام فراخوانی یک تابع، به آن ارائه می‌شود. زمانی که تابع را فراخوانی می‌کنیم، مقدار مورد نظر خود را در داخل پرانتزهای آن قرار می‌دهیم. در این مورد نشان‌وند 'jesse' به تابع greet_user() منتقل شده و این مقدار به پارامتر username منسوب شده است.

نکته
افراد گاهی نشان‌وند و پارامتر را به جای هم به کار می‌برند. اگر جایی دیدید که به متغیّرهای تعریف‌شده در تعریفِ تابع نشان‌وند یا به متغیرّهای فراخوانی تابع پارامتر گفته‌اند، تعجّب نکنید.

خودآزمایی

۸-۱. پیام: تابعی به نام display_message() بنویسید که جمله‌ای چاپ کند مبنی بر این که شما در این فصل مشغولِ آموختنِ چه هستید. تابع را فراخوانی کنید و مطمئن شوید که پیام به درستی نمایش داده می‌شود.

۸-۲. کتاب دلخواه: تابعی به نام favorite_book() بنویسید که یک پارامتر به نام title بپذیرد. این تابع باید پیامی چاپ کند مانند One of my favorite books is Alice in Wonderland. تابع را فراخوانی کنید و مطمئن شوید که هنگام فراخوانی، عنوانِ کتابی را به عنوانِ نشان‌وند به آن می‌دهید.

3 - انتقال نشان‌وندها

فراخوانی یک تابع ممکن است به نشان‌ونن73 یاز داشته باشد زیرا تعریف تابع ممکن است شامل چند پارامتر باشد. چند راه برای انتقال نشان‌وندها به تابع‌ها دارید. می‌توانید از نشان‌وندهای مکان‌دار استفاده کنید که هنگام انتقال به تابع باید با همان ترتیبِ تعریف شدهِ پارامترها وارد شوند. در نشان‌وندهای کلیدواژه‌ای هر نشان‌وند از نام متغیّر و یک مقدار، لیست یا دیکشنری‌هایی از مقدارها تشکیل شده است. در ادامه به هر یک از این دو مورد خواهیم پرداخت.

نشان‌وندهای مکان‌دار

هنگام فراخوانی یک تابع، پایتون باید هر نشان‌وند را با پارامتر متناظرش در تعریفِ تابع، جفت کند. ساده‌ترین راه، انجامِ این کار بر اساسِ ترتیبِ تعیینِ نشان‌وندهاست. مقدارهایی که به این ترتیب جفت می‌شوند نشان‌وندهای مکان‌دار نامیده می‌شوند.

برای این که نحوه‌ی این کار را ببینید تابعی را در نظر بگیرید که اطّلاعاتی را در مورد حیوانات خانگی چاپ می‌کند. این تابع می‌گوید که هر حیوان خانگی چه نوع جانوری است و نام آن چیست:

1
2
3
4
5
6
def describe_pet(animal_type, pet_name):
    """Display information about a pet."""
    print(f"\nI have a {animal_type}.")
    print(f"My {animal_type}'s name is {pet_name.title()}.")

describe_pet('hamster', 'harry')

تعریف تابع نشان می‌دهد که به نوع و نام حیوان نیاز دارد (۱). زمانی که describe_pet() را فراخوانی می‌کنیم، باید دو مقدار برای نوع و نام حیوان را به همان ترتیب ارائه کنیم. برای نمونه در این فراخوانی تابع، نشان‌وند 'hamster' به پارامتر animal_type و نشان‌وند 'harry' به پارامتر pet_name منسوب می‌شود. (۲) در بدنه‌ی تابع این دو پارامتر برای نمایش اطّلاعاتی در مورد حیوان مورد بحث ارائه می‌شود.

خروجی، همستری با نام Harry را شرح می‌دهد:

1
2
3
I have a hamster.

My hamster's name is Harry.

فراخوانی چندباره‌ی تابع

می‌توانید یک تابع را هر چند بار که نیاز داشته باشید فراخوانی کنید. توصیف حیوان خانگی متفاوت دوم تنها نیازمند فراخوانی دوباره‌ی describe_pet() است:

1
2
3
4
5
6
7
def describe_pet(animal_type, pet_name):
    """Display information about a pet."""
    print(f"\nI have a {animal_type}.")
    print(f"My {animal_type}'s name is {pet_name.title()}.")

describe_pet('hamster', 'harry')
describe_pet('dog', 'willie')

در دومین فراخوانیِ تابع دو نشان‌وندِ 'dog' و 'willie' را به describe_pet() دادیم. مشابه نشان‌وندهایی که پیش از این به کار بردیم، پایتون 'dog' را با پارامتر animal_type و 'willie' را با پارامتر pet_name تطبیق می‌دهد.

تابع همانند گذشته کار خود را انجام می‌دهد امّا این بار مقدارهایی را برای سگی به نام ویلی چاپ می‌کند. اکنون ما همستری به نام هری و سگی به نام ویلی داریم:

1
2
3
4
5
6
7
I have a hamster.

My hamster's name is Harry.

I have a dog.

My dog's name is Willie.

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

می‌توانید هر تعداد نشان‌وند مکان‌دار که نیاز داشته باشید در یک تابع استفاده کنید. پایتون نشان‌وندهایی را که ارائه کرده‌اید یکی یکی بررسی می‌کند و هر کدام را با پارامتر متناظر آن در تعریفِ تابع تطبیق می‌دهد.

در نشان‌وندهای مکان‌دار ترتیب اهمّیّت دارد

اگر هنگام به‌کارگیری نشان‌وندهای مکان‌دار ترتیب آن‌ها را به هم بریزید ممکن است به نتایج ناخواسته‌ای برسید:

1
2
3
4
5
6
def describe_pet(animal_type, pet_name):
    """Display information about a pet."""
    print(f"\nI have a {animal_type}.")
    print(f"My {animal_type}'s name is {pet_name.title()}.")

describe_pet('harry', 'hamster')

در این فراخوانیِ تابع نام را اول و نوع حیوان را پس از آن وارد کرده‌ایم. چون نشان‌وند 'harry' در ابتدا آمده است، مقدار آن به پارامتر animal_type منسوب می‌شود. به طور مشابه، 'hamster' به pet_name منسوب می‌شود. اکنون یک هری با نام همستر داریم:

1
2
3
I have a harry.

My harry's name is Hamster.

اگر به نتایج خنده‌داری مثل این رسیدید، بررسی کنید تا مطمئن شوید ترتیب نشان‌وندها هنگام فراخوانی تابع، به ترتیبِ پارامترها در تعریفِ تابع می‌خورد.

نشان‌وندهای کلیدواژه‌ای

نشان‌وند کلیدواژه‌ای جفتِ نام-مقداری است که به یک تابع منتقل می‌کنید. شما نام و مقدار را مستقیماً در نشان‌وند تعیین می‌کنید درنتیجه هنگام انتقال نشان‌وند به تابع هیچ گاه سردرگم نخواهید شد. (مانند مثال قبل به خروجیِ هری به نام همستر برنخواهید خورد). نشان‌وندهای کلیدواژه‌ای، نگرانیِ شما را در مورد حفظ ترتیب درستِ نشان‌وندها هنگام فراخوانی یک تابع از بین می‌برد و نقش هر مقدار را در فراخوانی تابع مشخّص و واضح می‌کند.

اکنون pets.py را با فراخوانی describe_pet() به کمک نشان‌وندهای کلیدواژه‌ای بازنویسی می‌کنیم:

1
2
3
4
5
6
def describe_pet(animal_type, pet_name):
    """Display information about a pet."""
    print(f"\nI have a {animal_type}.")
    print(f"My {animal_type}'s name is {pet_name.title()}.")

describe_pet(animal_type='hamster', pet_name='harry')

تابع describe_pet() تغییری نکرده است. امّا هنگام فراخوانی آن به طور صریح به پایتون می‌گوییم که کدام نشان‌وند باید با کدام پارامتر جفت شود. پایتون فراخوانیِ تابع را می‌خواند و می‌فهمد که نشان‌وند 'hamster' را به پارامتر animal_type و 'harry' را به pet_name نسبت دهد.

ترتیبِ نشان‌وندهای کلیدواژه‌ای اهمیتی ندارد چون پایتون می‌داند که هر مقدار باید به کجا برود. این دو فراخوانی تابع یکسان‌اند:

1
2
describe_pet(animal_type='hamster', pet_name='harry')
describe_pet(pet_name='harry', animal_type='hamster')
نکته
اگر از نشان‌وندهای کلیدواژه‌ای استفاده می‌کنید مطمئن شوید که در تعریفِ تابع نام دقیق پارامترها را وارد کنید.

مقدارهای پیش‌فرض 75

می‌توانید هنگام نوشتن یک تابع برای هر پارامتر آن یک مقدار پیش‌فرض تعیین کنید. اگر هنگام فراخوانیِ تابع برای یک نشان‌وند، پارامتری ارائه شود پایتون از مقدار آن استفاده می‌کند. اگر نه، مقدارِ پیش‌فرضِ پارامتر به کار گرفته می‌شود. بنابراین اگر برای یک پارامتر مقداری پیش‌فرض تعیین کنید، ارائه‌ی نشان‌وندِ متناظر هنگام فراخوانیِ تابع، الزامی نخواهد بود. به‌کارگیری مقدارهای پیش‌فرض فراخوانی توابع را ساده‌تر می‌کند و روش‌های معمول به‌کارگیری توابع را شفّاف‌تر می‌کند.

برای مثال اگر متوجّه شوید که بیشتر فراخوانی‌های تابع describe_pets() برای توصیف سگ‌ها به کار می‌روند، می‌توانید مقدار پیش‌فرض animal_type را 'dog' قرار دهید. حال هرکس هنگام فراخوانی describe_pet() برای یک سگ می‌تواند آن بخش از اطّلاعات ورودی را حذف کند.

1
2
3
4
5
6
def describe_pet(pet_name, animal_type='dog'):
    """Display information about a pet."""
    print(f"\nI have a {animal_type}.")
    print(f"My {animal_type}'s name is {pet_name.title()}.")

describe_pet(pet_name='willie')

تعریفِ describe_pet() را تغییر دادیم تا مقدار پیش‌فرضِ 'dog' را برای animal_type شامل شود. اکنون هرگاه تابع بدون تعیینِ animal_type فراخوانی شود، پایتون می‌داند که مقدار 'dog' را به جای این پارامتر به کار برد:

1
2
3
I have a dog.

My dog's name is Willie.

توجّه داشته باشید که ترتیب پارامترها در تعریفِ تابع باید تغییر یابد. از آن‌جایی که وجودِ مقدارِ پیش‌فرض، لزوم تعریف نوع حیوان را به عنوان پارامتر از بین برده، تنها نشان‌وندی که در فراخوانیِ تابع باقی می‌ماند نام حیوان خانگی است. پایتون این نشان‌وند را همچنان مکان‌دار تلقّی می‌کند پس اگر این تابع فقط با نام حیوان خانگی فراخوانده شود، آن نشان‌وند با اولّین پارامتری که در تعریفِ تابع آمده، جفت می‌شود. به این دلیل است که پارامتر اوّل باید pet_name باشد.

اکنون ساده‌ترین روش به‌کارگیری این تابع این است که هنگام فراخوانی آن تنها نام سگ را ارائه کنیم:

1
describe_pet('willie')

خروجیِ این فراخوانیِ تابع مشابه مثال قبل است. تنها نشان‌وندی که ارائه شده 'willie' است، بنابراین این مقدار با اوّلین پارامتر در تعریفِ تابع، pet_name، جفت می‌شود. از آن‌جایی‌که هیچ نشان‌وندی برای animal_type آورده نشده، پایتون از مقدارِ پیش‌فرضِ 'dog' استفاده می‌کند.

برای توصیف حیوانی به جز یک سگ، می‌توانید تابع را به این شکل فراخوانی کنید:

1
describe_pet(pet_name='harry', animal_type='hamster')

پایتون مقدارِ پیش‌فرضِ پارامتر را نادیده می‌گیرد چون این بار صریحاً نشان‌وندی برای animal_type تعیین شده است.

نکته
هنگامی که از مقدارهای پیش‌فرض استفاده می‌کنید، پارامترهایی که مقدار پیش‌فرض دارند باید بعد از پارامترهایی بیایند که برای آن‌ها مقدار پیش‌فرض تعریف نشده است. این کار به پایتون امکان می‌دهد که نشان‌وندهای مکان‌دار را به درستی تفسیر کند.

فراخوانی‌های تابع همسان

از آن‌جایی که نشان‌وندهای مکان‌دار، نشان‌وندهای کلیدواژه‌ای و مقدارهای پیش‌فرض می‌توانند همگی در کنار هم به کار روند، اغلب برای فراخوانی یک تابع چندین راه پیش رو دارید. تعریف زیر را برای describe_pet() با یک مقدارِ پیش‌فرض در نظر بگیرید:

1
def describe_pet(pet_name, animal_type='dog'):

با چنین تعریفی همواره باید نشان‌وندی به جای pet_name ارائه کرد و این مقدار می‌تواند به صورت مکان‌دار یا کلیدواژه‌ای باشد. اگر حیوانی که توصیف می‌شود، سگ نباشد، نشان‌وندی هم باید برای animal_type ارائه شود که آن نیز می‌تواند به شکل کلیدواژه‌ای یا مکان‌دار باشد.

تمام فراخوانی‌های زیر برای این تابع قابل استفاده‌اند:

1
2
3
4
5
6
7
8
# A dog named Willie.
describe_pet('willie')
describe_pet(pet_name='willie')

# A hamster named Harry.
describe_pet('harry', 'hamster')
describe_pet(pet_name='harry', animal_type='hamster')
describe_pet(animal_type='hamster', pet_name='harry')

تمام این فراخوانی‌های خروجی‌ای مشابه با قبل خواهند داشت.

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

پرهیز از خطاهای نشان‌وندی

هنگامی که به‌کارگیری تابع‌ها را آغاز می‌کنید، برخوردن به خطاهایی در رابطه با نشان‌وندهای نابه‌جا یا به درستی جفت‌نشده، نباید موجب شگفتی شما شود. این عدم تطابق زمانی رخ می‌دهد که شما نشان‌وندهایی کمتر یا بیشتر از آن‌چه تابع نیاز دارد، ارائه می‌کنید. برای مثال ببینید اگر describe_pets() را بدون هیچ نشان‌وندی فرابخوانیم چه اتّفاقی می‌افتد:

1
2
3
4
5
6
def describe_pet(animal_type, pet_name):
    """Display information about a pet."""
    print(f"\nI have a {animal_type}.")
    print(f"My {animal_type}'s name is {pet_name.title()}.")

describe_pet()

پایتون تشخیص می‌دهد که برخی اطّلاعات در فراخوانی تابع جا افتاده است و tracebackای در این مورد به ما اطّلاعات می‌دهد:

1
2
3
4
5
Traceback (most recent call last):
  File "pets.py", line 6, in <module>
    describe_pet()
TypeError: describe_pet() missing 2 required positional arguments:
    'animal_type' and 'pet_name'

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

این موضوع که پایتون کد تابع را می‌خواند و نام نشان‌وندهای جاافتاده را به ما ارائه می‌کند، سودمند است. این می‌تواند انگیزه‌ای باشد که برای تابع‌ها و متغیّرهای خود نام‌های گویا و پرمعنی انتخاب کنید. اگر چنین کنید پیام‌های خطای پایتون می‌تواند برای شما و هر‌کس که از کد شما استفاده می‌کند، مفیدتر باشد.

اگر تعداد نشان‌وندها بیش از حد نیاز باشد، به tracebackای مشابه برمی‌خورید که به شما کمک می‌کند فراخوانی تابع را با تعریفِ آن جور کنید.

خودآزمایی

۸-۳. تیشرت: تابعی به نام make_shirt() بنویسید که اندازه تیشرت و متن پیامی را که قرار است روی آن چاپ شود دریافت کند. این تابع باید پیامی چاپ کند که شامل پیامی خلاصه در مورد اندازه و جمله‌ی چاپ‌شده روی تیشرت باشد.

تابع را یک‌بار به کمک نشان‌وندهای مکان‌دار فراخوانی کنید تا اطلاعات تیشرتی را تولید کند. بار دیگر تابع را این بار به کمک نشان‌وندهای کلیدواژه‌ای فراخوانی کنید.

۸-۴. تیشرت‌های سایز بزرگ: تابع make_shirt() را طوری تغییر دهید که سایز تیشرت‌ها به صورت پیش‌فرض بزرگ و پیام چاپ‌شده I love Python باشد. یک تیشرت بزرگ و متوسط با پیام پیش‌فرض و یک تیشرت با پیامی متفاوت (و در هر سایزی که خواستید) تولید کنید.

۸-۵. شهرها: تابعی به نام describe_city() بنویسید که نام یک شهر و کشور آن را دریافت می‌کند. این تابع باید جمله‌ای ساده چاپ کند، مانند Reykjavik is in Iceland. به پارامتر مربوط به کشور یک مقدارِ پیش‌فرض بدهید. تابع را برای سه شهر مختلف فراخوانی کنید که دستِ کم در یکی از آن‌ها از کشور پیش‌فرض استفاده نشود.

4 - مقدارهای برگشتی

دلیلی ندارد که تابع همیشه خروجی را به طور مستقیم نمایش دهد. در عوض می‌تواند داده‌هایی را پردازش کند و مقدار یا مجموعه‌ای از مقدارها را برگرداند. به مقداری که تابع برمی‌گرداند مقدار برگشتی گویند. عبارت return مقداری را از داخل تابع می‌گیرد و به همان خطّی که تابع در آن فراخوانی شده بود برمی‌گرداند. با مقدارهای برگشتی می‌توان بخش زیادی از کارهای برنامه را به تابع‌ها منتقل کرد که این، باعث ساده‌تر شدن بدنه‌ی برنامه‌ی شما می‌شود.

بازگرداندن یک مقدار ساده

تابعی را می‌بینیم که نام و نام خانوادگی را می‌گیرد و نام کامل را به شکلی مرتّب برمی‌گرداند:

1
2
3
4
5
6
7
def get_formatted_name(first_name, last_name):
    """Return a full name, neatly formatted."""
    full_name = f"{first_name} {last_name}"
    return full_name.title()

musician = get_formatted_name('jimi', 'hendrix')
print(musician)

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

وقتی تابعِ تعریف‌شده مقداری را برمی‌گرداند باید متغیّری ارائه کنید که مقدار برگشتی به آن منسوب شود. در این مورد مقدار برگشتی به متغیّر musician نسبت داده می‌شود (۴). خروجی نامی مرتّب را نشان می‌دهد که از اجزای نام یک فرد تشکیل شده است:

1
Jimi Hendrix

به نظر می‌رسد که برای تولید یک نام به این شیوه کار زیادی انجام دادیم در حالی‌که می‌توانستیم به سادگی بنویسیم:

1
print("Jimi Hendrix")

امّا برنامه‌ای بزرگ را در نظر بگیرید که باید تعداد زیادی نام و نام خانوادگی را جداگانه ذخیره کند. در چنین برنامه‌ای تابع‌هایی مثل get_formatted_name() بسیار سودمند خواهد بود. نام‌ها و نام‌های خانوادگی را جداگانه ذخیره می‌کنید و سپس هرگاه نیاز داشتید آن را به صورت نام کامل نمایش می‌دهید.

اختیاری کردن یک نشان‌وند

اختیاری کردن تعیینِ یک نشان‌وند، گاهی کاری معقول است تا کاربران بتوانند اطّلاعات اضافی را تنها زمانی وارد کنند که به آن‌ها نیاز است. می‌توانید از مقدارهای پیش‌فرض برای اختیاری کردن یک نشان‌وند استفاده کنید.

برای مثال فرض کنید می‌خواهیم get_formatted_name() را طوری گسترش دهیم که نام‌های میانی را هم دربربگیرد. اولّین تلاش برای چنین کاری ممکن است اینگونه باشد:

1
2
3
4
5
6
7
def get_formatted_name(first_name, middle_name, last_name):
    """Return a full name, neatly formatted."""
    full_name = f"{first_name} {middle_name} {last_name}"
    return full_name.title()

musician = get_formatted_name('john', 'lee', 'hooker')
print(musician)

این تابع زمانی کار می‌کند که نام، نام میانی و نام خانوادگی به آن داده شود. تابع هر سه بخش نام را گرفته و با آن‌ها رشته‌ای می‌سازد. سپس جاهایی که نیاز باشد فاصله اضافه کرده و نام کامل را به حالت عنوان در می‌آورد:

1
John Lee Hooker

امّا نام میانی همواره مورد نیاز نیست و این تابع جوری نوشته شده که اگر فقط نام و نام خانوادگی را به آن بدهید کار نمی‌کند. برای اختیاری کردن نام میانی می‌توانیم به نشان‌وند middle_name یک مقدار پیش‌فرض خالی بدهیم و از آن جز در مواردی که کاربر مقدار ارائه کرده، صرف نظر کنیم. برای این که کاری کنیم که get_formatted_name() بدون نام میانی کار کند، مقدار پیش‌فرض middle_name را یک رشته‌ی خالی می‌گذاریم و آن را به انتهای فهرست پارامترها منتقل می‌کنیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def get_formatted_name(first_name, last_name, middle_name=''):
    """Return a full name, neatly formatted."""
    if middle_name:
        full_name = f"{first_name} {middle_name} {last_name}"
    else:
        full_name = f"{first_name} {last_name}"
    return full_name.title()

musician = get_formatted_name('jimi', 'hendrix')
print(musician)

musician = get_formatted_name('john', 'hooker', 'lee')
print(musician)

در این مثال نام از سه بخش تشکیل شده است. چون نام و نام خانوادگی همواره باید ارائه شوند، این دو پارامتر را در تعریفِ تابع در ابتدا آورده‌ایم. نام میانی اختیاری است بنابراین پس از این دو پارامتر می‌آید و مقدار پیش‌فرض آن رشته‌ای خالی است. (۱)

در بدنه‌ی تابع بررسی می‌کنیم که آیا نام میانی وارد شده یا نه. پایتون رشته‌های ناتُهی را True تلقّی می‌کند پس اگر middle_name درست (True) ارزیابی شود به این معنی است که هنگام فراخوانی تابع برای آن مقداری تعیین شده است. (۲). اگر نام میانی ارائه شده باشد، نام، نام میانی و نام خانوادگی با هم ترکیب می‌شوند تا نام کامل ساخته شود. نام کامل به حالت عنوان درآمده و به خطّ فراخوانی تابع برگردانده می‌شود؛ جایی که مقدار آن به متغیّر musician نسبت داده شده و چاپ می‌شود. اگر نام میانی وارد نشده باشد، آزمون شرطی if رد می‌شود و بخش else اجرا می‌شود (۳). نام کامل تنها با نام و نام خانوادگی ساخته می‌شود و نام شکل‌داده‌شده به خط فراخوانی برگردانده شده و چاپ می‌شود.

فراخوانی تابع با نام و نام خانوادگی سرراست است. اگر بخواهیم نام میانی را هم وارد کنیم، باید مطمئن شویم که آن را به عنوان آخرین نشان‌وند بنویسیم تا پایتون بتواند نشان‌وندهای مکان‌دار را به درستی تطبیق دهد. (۴)

نسخه‌ی تغییریافته‌ی تابع برای افرادی که تنها نام و نام خانوادگی دارند کار می‌کند، و به همان ترتیب برای افرادی که نام میانی هم دارند، عمل می‌کند:

1
2
3
Jimi Hendrix

John Lee Hooker

مقدارهای اختیاری این امکان را به تابع‌ها می‌دهد که کاربردهای بسیار متنوّعی را پوشش دهند در عین حالی که فراخوانی آن‌ها تا جای ممکن ساده باشد.

بازگرداندن یک دیکشنری

یک تابع می‌تواند هر نوع مقداری را که نیاز داشته باشید برگرداند، از جمله ساختمان‌های داده‌ی پیچیده‌تر مانند لیست‌ها و دیکشنری‌ها. برای مثال تابع زیر اجزای نام را می‌گیرد و یک دیکشنری را که نماینده‌ی یک فرد است برمی‌گرداند:

1
2
3
4
5
6
7
def build_person(first_name, last_name):
    """Return a dictionary of information about a person."""
    person = {'first': first_name, 'last': last_name}
    return person

musician = build_person('jimi', 'hendrix')
print(musician)

تابع build_person() نام و نام خانوادگی را می‌گیرد و این مقدارها را در ۱ در یک دیکشنری قرار می‌دهد. مقدار first_name در کلید 'first' و مقدار last_name در کلید 'last' ذخیره می‌شود. کل دیکشنری که نماینده‌ی یک فرد است در ۲ برگردانده می‌شود. مقدار برگشتی در ۳ چاپ می‌شود که نشان‌دهنده‌ی دو دانسته‌ی متنی است که این بار در یک دیکشنری ذخیره شده است:

1
{'first': 'jimi', 'last': 'hendrix'}

این تابع دو داده‌ی متنی ساده را دریافت کرده و آن در یک ساختمان داده‌ی بامعناتر قرار می‌دهد که به شما امکان می‌دهد با داده‌ها کارهایی فراتر از چاپ کردن بکنید. رشته‌های 'jimi' و 'hendrix' اکنون به عنوان نام و نام خانوادگی برچسب خورده‌اند. می‌توانید این تابع را به سادگی گسترش دهید تا مقدارهای اختیاری مختلفی بگیرد؛ همچون نام میانی، سن، شغل و یا هر اطّلاعاتی دیگری که بخواهید در مورد یک فرد ذخیره کنید. برای مثال تغییر زیر موجب می‌شود که بتوانید سن فرد را هم ذخیره کنید:

1
2
3
4
5
6
7
8
9
def build_person(first_name, last_name, age=None):
    """Return a dictionary of information about a person."""
    person = {'first': first_name, 'last': last_name}
    if age:
        person['age'] = age
    return person

musician = build_person('jimi', 'hendrix', age=27)
print(musician)

پارامتر اختیاری سن را به تعریفِ تابع افزوده‌ایم و77 به آن مقدار ویژه‌ی None را نسبت داده‌ایم. این مقدار زمانی استفاده می‌شود که متغیّر هیچ مقدار خاصّی ندارد. به None می‌توانید به عنوان یک نگهدارنده یا جای‌بان نگاه کنید. در آزمون‌های شرطی None نادرست (False) ارزیابی می‌شود. اگر هنگام فراخوانیِ تابع سن وارد شود، مقدار آن در دیکشنری ذخیره می‌گردد. این تابع همواره نام فرد را ذخیره می‌کند امّا می‌توان آن را جوری تغییر داد که هر اطّلاعات دیگری را در مورد یک فرد در بر بگیرد.

به‌کارگیری تابع با یک حلقه‌ی while

می‌توانید از تابع‌ها در تمام ساختارهای پایتون که تاکنون آموخته‌اید استفاده کنید. برای مثال بیایید از تابع get_formatted_name() به همراه یک حلقه‌ی while استفاده کنیم تا به کاربران کمی رسمی‌تر سلام کنیم. این اوّلین تلاش برای سلام کردن به افراد با نام و نام خانوادگی آن‌هاست:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
def get_formatted_name(first_name, last_name):
    """Return a full name, neatly formatted."""
    full_name = f"{first_name} {last_name}"
    return full_name.title()

# This is an infinite loop!
while True:
    print("\nPlease tell me your name:")
    f_name = input("First name: ")
    l_name = input("Last name: ")
    formatted_name = get_formatted_name(f_name, l_name)
    print(f"\nHello, {formatted_name}!")

در این مثال از نسخه‌ی ساده‌تر get_formatted_name() استفاده کرده‌ایم که نام‌های میانی را شامل نمی‌شود. حلقه‌ی while از کاربران نام و نام خانوادگی آن‌ها را می‌پرسد. گرفتن نام و نام خانوادگی جداگانه انجام می‌پذیرد. (۱)

امّا این حلقه‌ی while یک مشکل دارد: شرط خروج برای آن تعیین نکرده‌ایم. هنگامی که چند ورودی داریم شرط خروجی را باید در کجا قرار دهیم؟ ما می‌خواهیم که کاربر قادر باشد به ساده‌ترین راه ممکن از برنامه خارج شود پس هر کدام از اعلان‌ها باید راهی برای خروج داشته باشند. عبارت break روش سرراستی برای خروج از حلقه در هر یک از اعلان‌هاست:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
def get_formatted_name(first_name, last_name):
    """Return a full name, neatly formatted."""
    full_name = f"{first_name} {last_name}"
    return full_name.title()

while True:
    print("\nPlease tell me your name:")
    print("(enter 'q' at any time to quit)")
    f_name = input("First name: ")
    if f_name == 'q':
        break
    l_name = input("Last name: ")
    if l_name == 'q':
        break
    formatted_name = get_formatted_name(f_name, l_name)
    print(f"\nHello, {formatted_name}!")

پیامی اضافه کرده‌ایم که به کاربر اطّلاع دهد چگونه می‌تواند خارج شود و سپس اگر کاربر مقدار خروج را در هرکدام از اعلان‌ها وارد کرد، حلقه را می‌شکنیم. اکنون برنامه به سلام کردن به افراد ادامه می‌دهد تا جایی که کسی 'q' را به جای هر کدام از نام‌ها وارد کند:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Please tell me your name:
(enter 'q' at any time to quit)
First name: **eric**
Last name: **matthes**

Hello, Eric Matthes!

Please tell me your name:
(enter 'q' at any time to quit)
First name: **q**

خودآزمایی

۸-۶. نام شهرها: تابعی به نام city_country() بنویسید که نام و کشور یک شهر را بگیرد. تابع باید رشته‌ای به شکل زیر برگرداند:

1
"Santiago, Chile"

تابع خود را با دستِ کم سه جفت شهر-کشور فراخوانی کنید و مقدارهای خروجی را چاپ کنید.

۸-۷. آلبوم: تابعی به نام make_album() بنویسید که یک دیکشنری برای توصیف یک آلبوم موسیقی می‌سازد. این تابع باید نام هنرمند و عنوان آلبوم را بگیرد و یک دیکشنری شامل این اطّلاعات را برگرداند. از تابع برای ساختن سه دیکشنری که هر کدام بیانگر یک آلبوم موسیقی است، استفاده کنید. مقدار برگشتی هر کدام را چاپ کنید تا نشان دهید دیکشنری‌ها اطّلاعات آلبوم را به درستی ذخیره می‌کنند.

از None برای اضافه کردن یک پارامتر اختیاری به make_album() استفاده کنید تا امکان ذخیره کردن تعداد آهنگ‌های یک آلبوم را هم فراهم آورد. اگر در خطّ فراخوانی مقداری هم برای تعداد آهنگ‌ها وارد شود، آن مقدار را به دیکشنری آلبوم اضافه کنید. حداقل یک فراخوانیِ تابع اضافه کنید که شامل تعداد آهنگ‌های یک آلبوم باشد.

۸-۸. آلبوم‌های کاربران: با برنامه‌ی تمرین ۸-۷ شروع کنید. یک حلقه‌ی while بنویسید که به کاربران امکان دهد نام و نام هنرمند یک آلبوم را وارد کنند. به محض دریافت اطّلاعات make_album() را با اطّلاعات کاربران فراخوانی کنید و دیکشنری‌ای را که ساخته شده نمایش دهید. مطمئن شوید که یک مقدار خروج در حلقه‌ی while تعیین کرده‌اید.

5 - انتقال لیست

انتقال لیست‌ها به یک تابع غالباً کاری سودمند است، چه لیستی از نام‌ها باشد، چه اعداد یا چیزهای پیچیده‌تر مثل دیکشنری‌ها. زمانی که لیستی را به تابعی می‌دهید، تابع به محتویّات آن دسترسی مستقیم دارد. اکنون از تابع‌ها برای کار کردن کارآمدتر با لیست‌ها استفاده می‌کنیم.

فرض کنید که لیستی از کاربران داریم و می‌خواهیم به هر کدامشان سلام کنیم. مثال زیر لیستی از نام‌ها را به تابعی به نام greet_users() می‌فرستد که به هر کدام از افراد داخل لیست به طور جداگانه سلام می‌کند.

1
2
3
4
5
6
7
8
def greet_users(names):
    """Print a simple greeting to each user in the list."""
    for name in names:
        msg = f"Hello, {name.title()}!"
        print(msg)

usernames = ['hannah', 'ty', 'margot']
greet_users(usernames)

تابع greet_users() را طوری تعریف کرده‌ایم که لیستی از نام‌ها را بپذیرد و آن را به پارامتر names نسبت دهد. تابع داخل لیست دریافتی حلقه می‌زند و به هر کدام از کاربرها سلام می‌کند. در ۱ لیستی از کاربران تعریف کرده‌ایم و آن را (usernames) هنگام فراخوانی به تابع greet_users() داده‌ایم:

1
2
3
4
5
Hello, Hannah!

Hello, Ty!

Hello, Margot!

این همان خروجی‌ای است که می‌خواستیم. هر کاربر پیامی شخصی دریافت می‌کند و شما می‌توانید تابع را هر زمان برای سلام کردن به گروه مشخّصی از کاربران به کار بگیرید.

تغییر دادن یک لیست در داخل تابع

زمانی که لیستی را به یک تابع ارسال می‌کنید، آن تابع می‌تواند آن را تغییر دهد. تغییراتی که در داخل بدنه‌ی تابع روی لیست اعمال می‌کنید دائمی‌اند که این باعث می‌شود بتوانید با تعداد بسیار زیاد داده هم به خوبی کار کنید.

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Start with some designs that need to be printed.
unprinted_designs = ['phone case', 'robot pendant', 'dodecahedron']
completed_models = []

# Simulate printing each design, until none are left.
# Move each design to completed_models after printing.
while unprinted_designs:
    current_design = unprinted_designs.pop()
    print(f"Printing model: {current_design}")
    completed_models.append(current_design)

# Display all completed models.
print("\nThe following models have been printed:")
for completed_model in completed_models:
    print(completed_model)

این برنامه با دو لیست شروع می‌شود: یکی لیستِ طرح‌هایی که باید چاپ شوند و دیگری لیستی خالی که طرح‌ها پس از چاپ شدن به آن منتقل می‌شوند. تا زمانی که طرحی در unprinted_designs باقی مانده باشد، حلقه‌ی while با خارج کردن طرح از انتهای لیست، ذخیره کردن آن در current_design و چاپ کردن پیامی مبنی بر در حالِ چاپ بودنِ طرح فعلی، عملیّات چاپ را شبیه‌سازی می‌کند. حلقه پس از آن طرح را به لیستِ مدل‌های کامل‌شده اضافه می‌کند. زمانی که حلقه به پایان رسید، فهرستی از طرح‌هایی که چاپ شده‌اند، نمایش داده می‌شود:

1
2
3
4
5
6
7
8
Printing model: dodecahedron
Printing model: robot pendant
Printing model: phone case

The following models have been printed:
dodecahedron
robot pendant
phone case

می‌توانیم این کد را با نوشتن دو تابع که هر یک کار خاصّی انجام می‌دهند، بازآرایی کنیم. بیشترِ کد تغییر نخواهد کرد؛ تنها آن را با دقّت بیشتری ساختاردهی می‌کنیم. تابع اوّل چاپ کردن طرح‌ها را مدیریّت می‌کند و دومی کارهای چاپی را که انجام شده خلاصه می‌کند:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
def print_models(unprinted_designs, completed_models):
    """Simulate printing each design, until none are left.
    Move each design to completed_models after printing."""
    while unprinted_designs:
        current_design = unprinted_designs.pop()
        print(f"Printing model: {current_design}")
        completed_models.append(current_design)


def show_completed_models(completed_models):
    """Show all the models that were printed."""
    print("\nThe following models have been printed:")
    for completed_model in completed_models:
        print(completed_model)


unprinted_designs = ['phone case', 'robot pendant', 'dodecahedron']
completed_models = []

print_models(unprinted_designs, completed_models)
show_completed_models(completed_models)

در ۱ تابع print_models() را با دو پارامتر تعریف می‌کنیم: لیستی از طرح‌هایی که باید چاپ شوند و لیستی از مدل‌های تکمیل‌شده. تابع با گرفتنِ این دو لیست، فرآیند چاپ را با خالی کردنِ لیست طرح‌های چاپ‌نشده و پر کردنِ لیستِ مدل‌های تکمیل‌شده شبیه‌سازی می‌کند. در ۲ تابع show_completed_models() را با یک پارامتر تعریف می‌کنیم: لیستِ مدل‌های تکمیل‌شده. تابع show_completed_models() با داشتنِ این لیست، نام هر کدام از مدل‌های چاپ‌شده را نمایش می‌دهد.

خروجیِ این برنامه مشابه نسخه‌ی بدون تابعِ آن است امّا کد آن بسیار سازمان‌یافته‌تر است. کدی که بیشترِ کارها را انجام می‌دهد به دو تابع مجزّا منتقل شده است که باعث می‌شود درک بخش اصلی برنامه ساده‌تر باشد. به بدنه‌ی برنامه نگاه کنید تا ببینید که درک آن‌چه که انجام می‌دهد چه‌قدر ساده‌تر است:

1
2
3
4
5
unprinted_designs = ['phone case', 'robot pendant', 'dodecahedron']
completed_models = []

print_models(unprinted_designs, completed_models)
show_completed_models(completed_models)

لیستی از طرح‌های چاپ‌نشده و یک لیست خالی که مدل‌های تکمیل‌شده را نگه می‌دارد تعریف می‌کنیم. پس از آن، چون قبلاً دو تابع خود را تعریف کرده‌ایم، تنها آن‌ها را با پارامترهای درست فراخوانی کنیم. تابع print_models() را صدا کرده و دو لیست مورد نیاز را به آن می‌دهیم؛ چنان‌که انتظار داشتیم این تابع چاپ کردن طرح‌ها را شبیه‌سازی می‌کند. سپس show_completed_models() را فراخوانی کرده و لیست مدل‌های تکمیل‌شده را به آن می‌دهیم تا بتواند چاپ شدنِ آن‌ها را گزارش کند. این نام‌های تابع گویا به دیگران این امکان را می‌دهد که کد را حتّی بدون توضیحات، خوانده و درک کنند.

نگه‌داری و توسعه‌ی این برنامه از نسخه‌ی بدونِ تابعِ آن ساده‌تر است. اگر بخواهیم بعداً طرح‌های دیگری را چاپ کنیم به سادگی تابع print_models() را دوباره فرامی‌خوانیم. اگر متوجّه شویم که کد مربوط به چاپ کردن باید تغییر کند، کد را یک بار تغییر می‌دهیم و هر جا که تابع استفاده شده باشد، تمام تغییراتِ اعمالی اعمال می‌شوند. این روش از تغییر دادن کد در جای جایِ برنامه کارآمدتر است.

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

منعِ یک تابع از تغییر دادن یک لیست

گاهی شما می‌خواهید مانع تغییر یافتنِ یک لیست در تابع شوید. برای نمونه فرض کنید می‌خواهید از لیست طرح‌های چاپ‌نشده شروع کنید و تابعی بنویسید که مانند مثال قبل آن‌ها را به لیست مدل‌های تکمیل‌شده منتقل کند. شاید تصمیم بگیرید علاوه بر چاپ کردنِ تمام طرح‌ها، لیست اصلی طرح‌های چاپ‌نشده را برای بایگانی نگه دارید. امّا از آن‌جایی که نام تمامِ طرح‌ها را از unprinted_designs خارج کرده‌اید، این لیست اکنون خالی است و لیستِ خالی تنها نسخه‌ای است که در اختیار دارید؛ لیست اصلی از بین رفته است. در چنین حالتی می‌توانید مشکل را با ارسال یک کپی از لیست (نه اصلِ آن) به تابع حل کنید. هر تغییری که تابع در لیست ایجاد کند تنها کپی آن را تحت تاثیر قرار می‌دهد و لیست اصلی دست‌نخورده باقی می‌ماند.

می‌توانید کپی‌ای از لیست را به این صورت به تابع ارسال کنید:

1
function_name(list_name[:])

نماد برش [:] یک کپی از لیست را به تابع می‌فرستد. اگر قصد نداشتیم که لیست طرح‌های چاپ‌نشده را در printing_models.py دست بزنیم، باید print_models() را مانند زیر فراخوانی می‌کردیم:

1
print_models(unprinted_designs[:], completed_models)

تابع print_models() می‌تواند به کار خود برسد زیرا همچنان نام تمام طرح‌های چاپ‌نشده را دریافت می‌کند. امّا این بار این از کپیِ لیست اصلی طرح‌های چاپ‌نشده استفاده می‌کند و نه لیستِ unprinted_designs واقعی. لیست completed_models مثل قبل با نام مدل‌های چاپ‌شده پر می‌شود امّا لیستِ اصلیِ طرح‌هایِ چاپ‌نشده در این تابع تغییری نمی‌کند.

اگرچه با ارسال یک کپی از لیست به تابع می‌توانید محتویّات آن را حفظ کنید، امّا تا زمانی که دلیل مشخّصی برای ارسال کپی ندارید، باید اصلِ لیست را بفرستید. کار کردن با لیستی که قبلاً ایجاد شده برای یک تابع کارآمدتر است زیرا از صرف زمان و حافظه برای ساختن یک لیست جداگانه پرهیز می‌کند، به ویژه هنگامی که با لیست‌های بزرگ سروکار دارید.

خودآزمایی

۸-۹. پیامک‌ها: لیستی شامل تعدادی پیامک بسازید. لیست را به تابع show_message() بفرستید تا هر پیامک را چاپ کند.

۸-۱۰. ارسال پیامک‌ها: از یک کپی از برنامه‌ی تمرین ۸-۹ شروع کنید. تابعی به نام send_messages() بنویسید که هر پیامک را چاپ کرده و آن را پس از چاپ به لیستی جدید به نام sent_messages منتقل می‌کند. پس از فراخوانی تابع هر دو لیست را چاپ کنید تا مطمئن شوید که پیامک‌ها به درستی منتقل شده‌اند.

۸-۱۱. پیامک‌های بایگانی‌شده: کار خود را از تمرین ۸-۱۰ شروع کنید. تابع send_messages() را با یک کپی از لیست پیامک‌ها فراخوانی کنید. بعد از صدا کردنِ تابع هر دو لیست را چاپ کنید و نشان دهید که لیست اصلی پیامک‌های خود را حفظ کرده است.

6 - انتقال تعداد دلخواهی نشان‌وند

گاهی از تعداد نشان‌وندهایی که یک تابع نیاز دارد، از پیش اطّلاع ندارید. خوشبختانه پایتون این امکان را می‌دهد که تابع تعداد دلخواهی نشان‌وند را هنگام فراخوانی دریافت کند.

برای مثال تابعی را در نظر بگیرید که پیتزا درست می‌کند. این تابع به تعداد مواد روی پیتزا نیاز دارد ولی شما از پیش نمی‌دانید که فرد چه تعدادی از این مواد را درخواست می‌کند. تابع مثال زیر یک پارامتر دارد، *toppings، امّا این پارامترها تمام نشان‌وندهایی را که در خط فراخوانی ارائه می‌شود، جمع‌آوری می‌کند:

1
2
3
4
5
6
def make_pizza(*toppings):
    """Print the list of toppings that have been requested."""
    print(toppings)

make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')

علامت ستاره در نام پارامتر *toppings به پایتون می‌گوید که یک چندتایی به نام toppings درست کن و تمام مقدارهایی را که دریافت می‌شود درونِ آن قرار بده. خروجیِ تولیدشده با فراخوانی print() در بدنه‌ی تابع نشان می‌دهد که پایتون می‌تواند فراخوانیِ تابع را با یک یا سه مقدار مدیریّت کند و با فراخوانی‌های مختلف یک جور برخورد می‌کند. توجّه کنید که پایتون نشان‌وندها را در یک چندتایی قرار می‌دهد، حتّی اگر تابع تنها یک مقدار ورودی داشته باشد:

1
2
('pepperoni',)
('mushrooms', 'green peppers', 'extra cheese')

اکنون می‌توانیم print() را با یک حلقه جایگزین کنیم که در مواد روی پیتزا می‌چرخد و پیتزایی را که سفارش داده شده توصیف می‌کند:

1
2
3
4
5
6
7
8
def make_pizza(*toppings):
    """Summarize the pizza we are about to make."""
    print("\nMaking a pizza with the following toppings:")
    for topping in toppings:
        print(f"- {topping}")

make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')

تابع چه یک مقدارِ ورودی بگیرد چه سه مقدار، پاسخِ متناسبی می‌دهد:

1
2
3
4
5
6
7
Making a pizza with the following toppings:
- pepperoni

Making a pizza with the following toppings:
- mushrooms
- green peppers
- extra cheese

این شکلِ دستوری صرف نظر از تعداد نشان‌وندهایی که تابع دریافت کرده، کار می‌کند.

ترکیب کردن نشان‌وندهای مکان‌دار و اختیاری

اگر بخواهید که تابعی انواعِ مختلفی نشان‌وند را بپذیرد، پارامتری که برای تعداد نشان‌وندهای اختیاری تعریف می‌شود، باید هنگام تعریفِ تابع پس از دیگر پارامترها قرار بگیرد. پایتون ابتدا نشان‌وندهای مکان‌دار و کلیدواژه‌ای را جفت و سپس در پارامتر آخر تمام نشان‌وندهای باقیمانده را جمع‌آوری می‌کند.

برای مثال اگر تابع به پارامتری برای اندازه‌ی پیتزا هم نیاز داشته باشد، باید آن را پیش از پارامتر *toppings بیاوریم:

1
2
3
4
5
6
7
8
def make_pizza(size, *toppings):
    """Summarize the pizza we are about to make."""
    print(f"\nMaking a {size}-inch pizza with the following toppings:")
    for topping in toppings:
        print(f"- {topping}")

make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

پایتون در تعریفِ تابع، اوّلین مقداری را که دریافت می‌کند به پارامتر size نسبت می‌دهد. تمام مقدارهای دیگر که پس از آن می‌آیند در چندتایی toppings ذخیره می‌شوند. فراخوانی‌های این تابع ابتدا شامل نشان‌وندی برای اندازه‌ی پیتزا هستند و پس از هر تعداد مواد روی پیتزا که مورد نیاز باشد، به تابع ارسال می‌شود.

اکنون هر پیتزا یک اندازه و تعدادی مواد دلخواه دارد و هر بخش اطّلاعات در جای مناسب خود چاپ می‌شود که نمایانگر اندازه در ابتدا و سپس مواد روی پیتزاست:

1
2
3
4
5
6
7
Making a 16-inch pizza with the following toppings:
- pepperoni

Making a 12-inch pizza with the following toppings:
- mushrooms
- green peppers
- extra cheese
نکته
شما اغلب پارامتری عمومی به نام *args می‌بینید که تمام نشان‌وندهای مکان‌دار مانند این را جمع‌آوری می‌کند.

به‌کارگیری نشان‌وندهای کلیدواژه‌ای اختیاری

گاهی می‌خواهید تعدادی نشان‌وند اختیاری را بپذیرید امّا از پیش نمی‌دانید که چه نوع اطّلاعاتی قرار است به تابع ارسال شود. در این مورد می‌توانید تابع‌هایی بنویسید که هر تعداد جفت کلید-مقدار را که هنگام فراخوانی ارائه شود، بپذیرد. یک نمونه هنگام ساختن نمایه‌ی کاربری است: شما می‌دانید که اطّلاعاتی در مورد یک کاربر دریافت می‌کنید امّا مطمئن نیستید آن‌ها از چه نوعی هستند. تابع build_profile() در مثال زیر همیشه نام و نام خانوادگی را دریافت می‌کند، امّا تعداد دلخواهی نشان‌وند کلیدواژه‌ای را هم می‌پذیرد:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
def build_profile(first, last, **user_info):
    """Build a dictionary containing everything we know about a user."""
    user_info['first_name'] = first
    user_info['last_name'] = last
    return user_info

user_profile = build_profile('albert', 'einstein',
    location='princeton',
    field='physics')
print(user_profile)

تعریف build_profile() انتظار دارد که نام و نام خانوادگی وارد شود و سپس اجازه می‌دهد که کاربر هر تعداد جفتِ نام-مقدار که بخواهید وارد کند. دو علامت ستاره پشت **user_info به پایتون می‌گوید که یک دیکشنری خالی به نام user_info بسازد و تمام جفت نام-مقدارهایی که دریافت می‌کند در آن قرار دهد. داخل تابع می‌توانید همانند هر دیکشنری دیگری، به جفت‌های کلید-مقدار user_info دسترسی داشته باشید.

در بدنه‌ی build_profile() همواره نام و نام خانوادگی را به دیکشنری user_info اضافه می‌کنیم زیرا دو دانسته را از کاربر دریافت می‌کنیم (۱) امّا باید آن‌ها را در دیکشنری قرار دهیم. سپس دیکشنری user_info را به خط فراخوانی تابع برمی‌گردانیم.

تابع build_profile() را فراخوانی می‌کنیم، نام 'albert'، نام خانوادگی 'einstein' و دو جفت کلید-مقدارِ location='princeton' و field='physics' را به آن انتقال می‌دهیم. profile برگردانده شده را به user_profile نسبت می‌دهیم و آن را چاپ می‌کنیم:

1
2
{'location': 'princeton', 'field': 'physics',
 'first_name': 'albert', 'last_name': 'einstein'}

دیکشنری برگشتی شامل نام و نام خانوادگی کاربر و در این مورد محل و حوزه‌ی مطالعاتی اوست. این تابع باید بتواند صرف نظر از تعداد جفت کلید-مقدارهای اضافیِ ارائه‌شده حین فراخوانی، کارکند.

نکته
اغلب به پارامتری به نام **kwargs برمی‌خورید که برای جمع‌آوری نشان‌وندهای کلیدواژه‌ای نامشخّص استفاده می‌شود.

خودآزمایی

۸-۱۲. ساندویچ‌ها: تابعی بنویسید که لیستی از مواد دلخواه داخل ساندویچ را از فرد بگیرد. تابع باید یک پارامتر داشته باشد که تمام مواردی را که در فراخوانی تابع ارائه می‌شود جمع‌آوری کند و خلاصه‌ای از ساندویچی که سفارش داده می‌شود، چاپ کند. تابع را سه بار فرابخوانید و هر بار تعداد متفاوتی نشان‌وند استفاده کنید.

۸-۱۳. نمایه‌ی کاربری: از نسخه‌ای از user_profile.py در صفحه‌ی *** شروع کنید. با فراخوانی تابع build_profile() برای خود نمایه‌ای بسازید که شامل نام و نام خانوادگی و سه جفت کلید-مقدار باشد که شما را توصیف می‌کند.

۸-۱۴. خودروها: تابعی بنویسید که اطّلاعات پیرامون یک خودرو را در یک دیکشنری ذخیره می‌کند. تابع باید همیشه نام سازنده و مدل خودرو را دریافت کند. پس از آن باید تعداد دلخواهی نشان‌وند کلیدواژه‌ای دریافت کند. تابع را با اطّلاعات الزامی و دو جفت نام-مقدار دیگر فرابخوانید، مانند یک رنگ یا ویژگی سفارشی. تابع شما باید با فراخوانی زیر کار کند:

1
car = make_car('subaru', 'outback', color='blue', tow_package=True)

دیکشنری برگشتی از تابع را چاپ کنید تا مطمئن شوید که اطّلاعات به درستی ذخیره شده‌اند.

7 - ذخیره کردن تابع‌ها در ماژول‌ها

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

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

چندین راه برای وارد کردن یک ماژول وجود دارد که هر کدام از آن‌ها به طور مختصر معرّفی می‌شود.

وارد کردن یک ماژول کامل

برای آغاز وارد کردن تابع‌ها، نیاز دارید ابتدا یک ماژول بسازید. یک ماژول فایلی با پسوند .py است و کدی را شامل می‌شود که می‌خواهید به برنامه‌ی خود وارد کنید. اکنون ماژولی می‌سازیم که حاوی تابع make_pizza() است. برای ساختن این ماژول همه‌چیز را به جز این تابع، از فایل pizza.py حذف می‌کنیم:

1
2
3
4
5
def make_pizza(size, *toppings):
    """Summarize the pizza we are about to make."""
    print(f"\nMaking a {size}-inch pizza with the following toppings:")
    for topping in toppings:
        print(f"- {topping}")

حال فایلی جداگانه به نام making_pizza.py در همان جایی که pizza.py را ذخیره کرده‌ایم می‌سازیم. این فایل ماژولی را که اکنون ساخته‌ایم وارد کرده و تابع make_pizza() را دوبار فراخوانی می‌کند.

1
2
3
4
import pizza

pizza.make_pizza(16, 'pepperoni')
pizza.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

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

برای فراخوانی تابعی از یک ماژولِ واردشده، نام ماژول را، در اینجا pizza، و پس از آن نام تابع را، در اینجا make_pizza() بنویسید و بین این دو یک نقطه قرار دهید (۱). خروجی این کد و برنامه‌ی اصلی که ماژولی را وارد نمی‌کرد، یکسان است:

1
2
3
4
5
6
7
Making a 16-inch pizza with the following toppings:
- pepperoni

Making a 12-inch pizza with the following toppings:
- mushrooms
- green peppers
- extra cheese

اوّلین رویکرد برای وارد کردن، که در آن به سادگی دستور import و نام ماژول را پس از آن می‌نویسید، تمام تابع‌های موجود در ماژول در برنامه‌ی شما در اختیار قرار می‌دهد. اگر این نوع import برای وارد کردن کامل یک ماژول به نام module_name.py استفاده کنید، هر تابع این ماژول از طریق دستور زیر در دسترس است:

1
module_name.function_name()

وارد کردن تابع‌های مشخّص

در عین حال می‌توانید تنها تابع خاصّی را از یک ماژول وارد کنید. این دستورِ کلی برای چنین رویکردی است:

1
from module_name import function_name

می‌توانید هر تعداد تابع را که خواستید از یک ماژول وارد کنید. برای این کار نام هر تابع را با یک کاما جدا کنید:

1
from module_name import function_0, function_1, function_2

اگر بخواهید تنها تابعی را که مورد نیاز است وارد کنیم، مثال making_pizzas.py به شکل زیر درمی‌آید:

1
2
3
4
from pizza import make_pizza

make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

با این دستور دیگر نیازی به استفاده از نقطه برای فراخوانی تابع ندارید. می‌توانیم هنگام استفاده از تابع آن را با نام فراخوانی کنیم، زیرا تابع make_pizza() را به طور صریح در عبارت import وارد کرده‌ایم.

استفاده از as برای تعیین نام مستعار 78 برای تابع

اگر نام تابعی که وارد می‌کنید با نام تابعی که در برنامه‌ی شما وجود دارد تداخل داشته باشد، یا نام آن تابع خیلی طولانی باشد، می‌توانید از یک نام مستعار کوتاه و یکتا استفاده کنید — نامی جایگزین که مشابه نام خودمانی برای یک تابع است. این نام مستعار را هنگام وارد کردن تابع به آن اطلاق می‌کنیم.

در اینجا با وارد کردنِ ماژول به صورت make_pizza as mp، به تابع make_pizza() نام مستعار mp() می‌دهیم. کلیدواژه‌ی as نام تابع را به نام مستعاری که تعیین کرده‌اید تغییر می‌دهد:

1
2
3
4
from pizza import make_pizza as mp

mp(16, 'pepperoni')
mp(12, 'mushrooms', 'green peppers', 'extra cheese')

عبارت import که اینجا نمایش داده شده نام تابع make_pizza() را در این برنامه به mp() تغییر می‌دهد. هر زمان که بخواهید می‌توانید برای فراخوانی به جای make_pizza() از mp() استفاده کنید و پایتون کد make_pizza() را اجرا خواهد کرد بدون این که با تابع make_pizza() دیگری که احتمالاً در برنامه‌ی خود تعریف کرده‌اید، هیچ گونه تداخلی به وجود بیاید.

دستور عمومی برای ارائه کردن نام مستعار به این صورت است:

1
from module_name import function_name as fn

استفاده از as برای تعیین نام مستعار برای یک ماژول

شما همچنین می‌توانید برای یک ماژول نام مستعار بگذارید. تعیین یک نام مستعار کوتاه، مانند p به جای pizza، باعث می‌شود که فراخوانی تابع‌های آن ماژول سریع‌تر انجام گیرد. فراخوانی p.make_pizza() بسیار خلاصه‌تر از pizza.make_pizza() است:

1
2
3
4
import pizza as p

p.make_pizza(16, 'pepperoni')
p.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

در عبارت import به ماژول pizza نام مستعار p داده‌ایم، امّا تابع‌های ماژول نام‌های اصلی خود را حفظ می‌کنند. فراخواندن تابع‌ها با نوشتن p.make_pizza() به جای pizza.make_pizza() نه تنها مختصرتر است بلکه توجّه شما از نام ماژول برمی‌گرداند و امکان می‌دهد که روی نام‌های گویای تابع‌های آن تمرکز کنید. نام این تابع‌ها به وضوح به شما می‌گویند که این تابع چه می‌کند و به‌کارگیری آن‌ها نسبت به به‌کارگیری نام کامل ماژول، برای خوانایی برنامه‌ی شما اهمّیّت بیشتری دارند.

دستور کلّی برای این رویکرد این است:

1
import module_name as mn

وارد کردن تمام تابع‌های یک ماژول

با استفاده از عملگر ستاره (*) می‌توانید از پایتون بخواهید که تمام تابع‌های یک ماژول را وارد کند:

1
2
3
4
from pizza import *

make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

ستاره در عبارت import به پایتون می‌گوید که تمام تابع‌های ماژول را در فایل برنامه کپی کن. چون تمام تابع‌ها وارد شده‌اند، می‌توانید آن‌ها را تنها با نام و بدون علامت نقطه استفاده کنید. با این حال بهتر آن است زمانی‌که با ماژول‌های بزرگتری که خود آن‌ها را ننوشته‌اید کار می‌کنید چنین کاری نکنید: اگر ماژول تابعی داشته باشد که با نامی در پروژه‌ی شما یکسان باشد، ممکن است به نتایج غیرمنتظره‌ای برسید. پایتون ممکن است تابع‌ها یا متغیّرهای مختلفی را با نام یکسان ببیند و به جای وارد کردن جداگانه‌ی تابع‌ها، آن‌ها را رونویسی کند.

بهترین رویکرد این است که تنها تابع یا توابعی را که می‌خواهید وارد کنید یا تمام ماژول را وارد کرده و از علامت نقطه استفاده کنید. این کار به کدی واضح منجر می‌شود که خواندن و درک کردن آن ساده است. این بخش از این جهت در اینجا آمده تا اگر به عبارت‌های import مانند این در کد دیگران برخوردید، بتوانید آن را تشخیص دهید:

1
from module_name import *

8 - شکل‌دهی به تابع‌ها

باید برخی جزئیات را هنگام شکل دادن به تابع‌ها در نظر داشته باشید. نام تابع‌ها باید گویا باشد و در آن‌ها از حروف کوچک و زیرخط استفاده شود. نام‌های گویا و بامعنی به شما و دیگران کمک می‌کند که بفهمید کد چه کاری انجام می‌دهد. نام ماژول‌ها هم باید از چنین قواعدی پیروی کنند.

هر تابع باید توضیحی داشته باشد که مختصراً بگوید تابع چه می‌کند. این توضیح باید بلافاصله بعد از تعریفِ تابع و به شکل docstring بیاید. اگر تابع به درستی مستند شده باشد، برنامه‌نویسان دیگر می‌توانند تنها با خواندن توضیحات موجود در docstring از آن استفاده کنند. آن‌ها باید قادر باشند که به کارکرد کد همانگونه که شرح داده شده اعتماد کنند و مادامی‌که بدانند نام تابع، نشان‌وندهایی که نیاز دارد و نوع مقداری که برمی‌گرداند، چیست باید بتوانند از آن در برنامه‌های خود استفاده کنند.

اگر برای پارامتری مقدار پیش‌فرض تعیین می‌کنید، هیچ فاصله‌ای نباید در اطراف علامت تساوی قرار بگیرد:

1
def function_name(parameter_0, parameter_1='default value'):

چنین قراردادی باید هنگام استفاده از نشان‌وندهای کلیدواژه‌ای در فراخوانی تابع هم مورد استفاده قرار گیرد:

1
function_name(value_0, parameter_1='value')

PEP 8 توصیه می‌کند که تعداد کاراکترهای هر خطّ کد را به ۷۹ عدد محدود کنید تا هرکدام از خط‌ها در پنجره‌ای با اندازه‌ی معقول قابل نمایش باشد. اگر مجموعه‌ای از پارامترها باعث می‌شوند که تعریفِ تابع بیش از ۷۹ کاراکتر شود، در تعریفِ تابع و پس از پرانتز باز یک ENTER بزنید. در خطّ بعدی دوبار TAB بزنید تا فهرست نشان‌وندها را از بدنه‌ی تابع، که یک مرحله تورفتگی دارد، جدا کنید.

اکثر ویرایشگرها خط‌های اضافیِ پارامترها را جوری تنظیم می‌کنند که با تورفتگی خطّ اول تناسب داشته باشد:

1
2
3
4
def function_name(
        parameter_0, parameter_1, parameter_2,
        parameter_3, parameter_4, parameter_5):
    function body...

اگر برنامه یا ماژول شما بیش از یک تابع دارد، می‌توانید آن‌ها را با دو خطّ خالی از یکدیگر جدا کنید تا پایان یک تابع و آغاز تابع بعدی آسان‌تر به چشم بیاید.

تمام عبارت‌های import باید در ابتدای یک فایل نوشته شوند. تنها استثنا این است که شما توضیحاتی در اوّل فایل برنامه بیاورید که کلیّت آن را شرح دهد.

خودآزمایی

۸-۱۵. چاپ کردن مدل‌ها: تابع‌های مثال printing_models.py را در یک فایل جداگانه به نام printing_functions.py قرار دهید. یک عبارت import بالای printing_models.py بنویسید و فایل را طوری تغییر دهید که از تابع‌های واردشده استفاده کند.

۸-۱۶. راه‌های مختلف وارد کردن: از یکی از برنامه‌های خود که یک تابع دارد استفاده و آن تابع را در یک فایل مجزّا ذخیره کنید. تابع را در برنامه‌ی اصلی خود وارد کنید و آن را با هر یک از رویکردهای زیر فراخوانی کنید:

1
2
3
4
5
6
7
8
9
import module_name

from module_name import function_name

from module_name import function_name as fn

import module_name as mn

from module_name import *

۸-۱۷. شکل‌دهی به تابع‌ها: سه برنامه از برنامه‌هایی که در این فصل نوشته‌اید انتخاب کنید و مطمئن شوید که در نوشتن آن‌ها شیوه‌نامه‌ی شکل‌دهی این بخش رعایت شده باشد.

خلاصه

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

یکی از هدف‌های شما به عنوان برنامه‌نویس نوشتنِ کد ساده برای انجام کارِ مورد نظرتان است و تابع‌ها به شما برای رسیدن به چنین هدفی کمک می‌کنند. آن‌ها این امکان را فراهم می‌آورند که قطعه‌کدهایی بنویسید و پس از این که فهمیدید کار می‌کنند آن‌ها را کناری بگذارید. زمانی که بفهمید تابعی وظیفه‌ی خود را درست انجام می‌دهد می‌توانید اعتماد کنید که همیشه درست کار خواهد کرد و به وظیفه‌ی برنامه‌نویسی بعدی خود بپردازید.

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

به‌کارگیری تابع‌ها خواندن برنامه‌های شما را ساده‌تر می‌کند و نام‌گذاریِ خوبِ تابع‌ها، کاری را که هر بخش برنامه انجام می‌دهد به طور خلاصه بیان می‌کند. خواندن دنباله‌ای از فراخوانی تابع‌ها درک سریع‌تری از برنامه می‌دهد تا این که بخواهید چندین بخشِ طولانی کد را بخوانید.

تابع‌ها همچنین آزمودن و اشکال‌زدایی کد شما را ساده‌تر می‌کند. وقتی کل کارِ برنامه‌ی شما در قالب تعدادی تابع انجام می‌شود که هر کدام وظیفه‌ی خاصّی دارند، آزمودن و نگهداری کدی که نوشته‌اید بسیار ساده‌تر خواهد بود. می‌توانید برنامه‌ای جداگانه بنویسید که هر تابع را فراخوانی کند و آن را بیازماید تا مشخّص شود که تابع در تمام وضعیّت‌هایی که ممکن است به آن بربخورد کار می‌کند یا نه. زمانی که چنین کنید مطمئن خواهید بود که تابع‌های شما در هر بار فراخوانی به خوبی کار می‌کنند.

در فصل ۹ نوشتنِ کلاس‌ها را می‌آموزید. کلاس‌ها تابع‌ها و داده‌ها را در یک بسته‌بندی منظّم قرار می‌دهند که می‌توانید از آن‌ها به روش‌های منعطف و کارآمدی استفاده کنید.