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

دلیلی ندارد که تابع همیشه خروجی را به طور مستقیم نمایش دهد. در عوض می‌تواند داده‌هایی را پردازش کند و مقدار یا مجموعه‌ای از مقدارها را برگرداند. به مقداری که تابع برمی‌گرداند مقدار برگشتی گویند. عبارت 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 تعیین کرده‌اید.