امضای تابع، قرارداد با فراخواننده
آرگومانها را میتوان به دو شکل فرستاد: موقعیتی (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 فقط موقعیتی |
*, a | a فقط با نام |
*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 میگیرید (برخلاف عملگر|). - آرگومانهای با نام را در فراخوانی به هر ترتیبی میتوانید بنویسید؛ پس در توابع با چند پارامتر، نامدار فرستادن جلوی جابهجا شدن عرض و طول را میگیرد.