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

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