فصل ۵: توابع — از تعریف ساده تا scope و بازگشت

*args، **kwargs، آرگومان keyword-only و positional-only

امضای تابع، قرارداد با فراخواننده

آرگومان‌ها را می‌توان به دو شکل فرستاد: موقعیتی (positional) که بر اساس ترتیب به پارامترها می‌رسند، و با نام (keyword) که بر اساس نام. rug_price(200, 300, 9_500_000) موقعیتی است و rug_price(width_cm=200, length_cm=300, price_per_sqm=9_500_000) با نام. در یک فراخوانی می‌توانید ترکیب کنید، به شرط این‌که موقعیتی‌ها اول بیایند.

*args: تعداد دلخواه آرگومان موقعیتی

def total(*prices, discount=0):
    # prices یک tuple است
    return sum(prices) * (100 - discount) // 100

print(total(8_500_000, 12_000_000))                 # 20500000
print(total(8_500_000, 12_000_000, discount=10))    # 18450000

ستاره در تعریف یعنی «همه‌ی آرگومان‌های موقعیتی باقی‌مانده را در یک tuple جمع کن». نام args فقط قرارداد است؛ *prices گویاتر است.

**kwargs: تعداد دلخواه آرگومان با نام

def make_label(code, **details):
    # details یک dict است
    parts = [f"{k}={v}" for k, v in details.items()]
    return f"{code} ({', '.join(parts)})"

print(make_label("KSH-101", color="لاکی", raj=50))
# KSH-101 (color=لاکی, raj=50)

keyword-only و positional-only

هر پارامتری که بعد از * (یا بعد از *args) بیاید، فقط با نام قابل ارسال است. هر پارامتری که قبل از / بیاید، فقط موقعیتی:

def make_order(code, /, qty, *, express=False):
    return code, qty, express

make_order("K1", 2)                    # درست
make_order("K1", qty=2, express=True)  # درست
make_order("K1", 2, True)              # TypeError: express باید با نام بیاید

چرا این محدودیت‌ها مفیدند؟ فراخوانی send_sms("0912...", "سلام", True, False) را در نظر بگیرید؛ True و False چه معنایی دارند؟ با keyword-only، فراخواننده مجبور است بنویسد urgent=True و کد خودش را توضیح می‌دهد. positional-only هم اجازه می‌دهد بعداً نام پارامتر را بدون شکستن کد دیگران عوض کنید.

نماد در تعریفمعنی
a, bمعمولی: موقعیتی یا با نام
a, /a فقط موقعیتی
*, aa فقط با نام
*argsبقیه‌ی موقعیتی‌ها در tuple
**kwargsبقیه‌ی با نام‌ها در dict؛ همیشه آخر

باز کردن هنگام فراخوانی

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

size = (200, 300)
options = {"price_per_sqm": 9_500_000}
rug_price(*size, **options)   # معادل rug_price(200, 300, price_per_sqm=9_500_000)

نکته‌هایی که کمتر کسی می‌داند

  • ترتیب مجاز در تعریف همیشه این است: موقعیتی‌فقط، /، معمولی، *args یا *، keyword-only، **kwargs.
  • خود print امضایی شبیه print(*objects, sep=" ", end="\n") دارد؛ برای همین print(*items, sep=" | ") لیست را با جداکننده چاپ می‌کند.
  • **kwargs راحت است ولی غلط‌های تایپی را می‌بلعد: make_label("K1", colour="قرمز") خطا نمی‌دهد. جایی که پارامترها مشخص‌اند، صریح نامشان را بنویسید.
  • برای ادغام دو دیکشنری هنگام فراخوانی: f(**defaults, **overrides)؛ اما اگر کلید تکراری باشد، TypeError می‌گیرید (برخلاف عملگر |).
  • آرگومان‌های با نام را در فراخوانی به هر ترتیبی می‌توانید بنویسید؛ پس در توابع با چند پارامتر، نام‌دار فرستادن جلوی جابه‌جا شدن عرض و طول را می‌گیرد.

برای ذخیره‌ی پیشرفت و شرکت در آزمون، وارد شوید — رایگان است.