نحوه استفاده از *args و **kwargs در پایتون ۳

مقدمه
در تعریف توابع، پارامترها موجودیتهای نامداری هستند که تعیین میکنند تابع چه آرگومانهایی را میتواند بپذیرد. وقتی لازم است تعداد متغیری از آرگومانها را بدون قفل کردن Signature پشتیبانی کنید، پایتون دو سینتکس خاص ارائه میدهد: *args برای آرگومانهای Positional اضافی و **kwargs برای آرگومانهای Keyword اضافی. این آموزش پوشش میدهد چطور توابع را با *args و **kwargs تعریف و فراخوانی کنید؛ چطور آنها را با پارامترهای استاندارد و فقط-Keyword ترکیب کنید؛ و چطور از آنها در Decoratorها، وراثت کلاس و کد Type-Annotateشده استفاده کنید.
نکات کلیدی
*argsآرگومانهای Positional اضافی را در Tupleی جمع میکند؛**kwargsآرگومانهای Keyword اضافی را در Dictی.- ترتیب قانونی آرگومانها در Signature تابع این است: پارامترهای Positional استاندارد، بعد
*args، بعد پارامترهای فقط-Keyword، بعد**kwargs. معکوس یا مخلوط کردن این ترتیب باعث SyntaxError میشود. - ستاره منفرد (
*) و ستاره دوتایی (**) عملگرهایی هستند که اهمیت دارند؛ نامهای args و kwargs فقط قراردادند. - میتوانید از
*و**در محل فراخوانی برای Unpack کردن Sequence یا Mapping به آرگومانهای Positional و Keyword هنگام صدا زدن تابع استفاده کنید. - حاشیهنویسیهای نوع (Type Annotation) برای
*argsو**kwargs(مثلاً*args: int،**kwargs: str) به هر عنصر یا مقدار اعمال میشوند؛ نه به کل Tuple یا Dict. - وقتی مجموعه آرگومانها ثابت و کوچک است، پارامترهای صریح را ترجیح دهید؛ از
*args/**kwargsبرای APIهای انعطافپذیر، Decoratorها و Forwarding استفاده کنید؛ و از سربار کوچکِ ساخت Tuple و Dict در مسیرهای داغ (Hot Path) آگاه باشید.
پیشنیازها
باید پایتون ۳ نصبشده و محیط برنامهنویسیای روی کامپیوتر یا سرورتان راهاندازیشده داشته باشید. اگر یکی ندارید، به راهنماهای نصب و راهاندازی محیط برنامهنویسی محلی یا محیط برنامهنویسی روی سرورتان برای سیستمعاملتان (اوبونتو، CentOS، دبیان و غیره) مراجعه کنید.
نکته: برای دنبال کردن کدهای نمونه در این آموزش، شل تعاملی پایتون را روی سیستم محلیتان با اجرای دستور
python3باز کنید. میتوانید مثالها را بعد از پرامپت>>>کپی، پیست یا ویرایش کنید.
درک آرگومانهای تابع در پایتون
توابع پایتون، آرگومانهای Positional (تطبیقیافته بر اساس موقعیت) و آرگومانهای Keyword (تطبیقیافته بر اساس نام) را میپذیرند. فهرستهای آرگومانِ طول-متغیر توسط *args و **kwargs مدیریت میشوند؛ که هر آرگومان Positional یا Keyword اضافی را در پارامتر منفردی جمع میکنند. تابعی مینویسید که دو عدد را جمع میکند. بعد لازم است سه تا را جمع کند. بعد چهار تا. با پارامترهای ثابت، هر تغییر Call Siteهای موجود را میشکند. *args و **kwargs این را با اجازه رشد Signature تابع بدون شکستن هیچچیز حل میکنند.
پارامترهای استاندارد در مقابل آرگومانهای متغیر
پارامترهای استاندارد نامها و تعداد ثابتی دارند. آرگومانهای متغیر به تابع اجازه میدهند صفر یا چند آرگومان Positional اضافی (*args) یا آرگومان Keyword اضافی (**kwargs) را بدون فهرست کردن تکتکشان در Signature بپذیرد. جدول زیر دو فرم متغیر را خلاصه میکند:
| ویژگی | *args | **kwargs |
|---|---|---|
| سینتکس | *args | **kwargs |
| ساختار داده | Tuple | Dict |
| نوع آرگومان | Positional | Keyword |
| روش Iteration | for val in args | `for k, v in kwargs.items()“ |
| Keyword در فراخوانی لازم است؟ | خیر | بله |
| هدف Type Annotation | هر عنصر | هر مقدار |
قوانین ترتیب آرگومانها
آرگومانها در تعریف تابع باید به ترتیب سختگیرانهای ظاهر شوند. جدول زیر ترتیب قانونی را نشان میدهد؛ نقض آن SyntaxError raised میکند:
| موقعیت | نوع | مثال |
|---|---|---|
| ۱ | Positional استاندارد | arg_1، arg_2 |
| ۲ | Positional متغیر | *args |
| ۳ | فقط-Keyword | kw_1=”default” |
| ۴ | Keyword متغیر | **kwargs |
هشدار: قرار دادن
**kwargsقبل از*argsفوراً SyntaxError میسازد. پایتون فایل را قبل از اجرای حتی یک خط رد میکند. مثلاً،def f(**kwargs, *args)تولید میکند:SyntaxError: invalid syntaxهمیشه از ترتیب قانونی پیروی کنید: Positional استاندارد، بعد
*args، بعد فقط-Keyword، بعد**kwargs.
نمونه Signature معتبری که هر چهار را ترکیب میکند:
full_example.py
def full_example(name, age, *args, role="user", **kwargs):
# name, age: پارامترهای positional الزامی
# *args: مقادیر positional اضافی را بهصورت tuple ثبت میکند
# role: پارامتر فقط-keyword با پیشفرض
# **kwargs: آرگومانهای keyword اضافی را بهصورت dict ثبت میکند
print(f"name={name}, age={age}")
print(f"extra positional args: {args}")
print(f"role={role}")
print(f"extra kwargs: {kwargs}")
full_example("Sam", 30, "extra1", "extra2", role="admin", team="infra", region="us-east")
python3 full_example.py
Output
name=Sam, age=30
extra positional args: ('extra1', 'extra2')
role=admin
extra kwargs: {'team': 'infra', 'region': 'us-east'}
استفاده از *args برای پذیرش آرگومانهای Positional متغیر
*args به تابع اجازه میدهد هر تعداد آرگومان Positional اضافی را بپذیرد. آنها در Tuple منفردی جمع میشوند که میتوانید رویش Iterate یا ایندکسش کنید.
*args درونزاد چگونه کار میکند
در سطح مفسر، ستاره همه آرگومانهای Positional باقیمانده را در Tupleای میبسته و به نام args متصل میکند. میتوانید این را در زمان اجرا تأیید کنید:
args_type_check.py
def inspect_args(*args):
# نوع و محتوای args را در زمان اجرا نشان بده
print(type(args)) # همیشه <class 'tuple'>
print(args) # مقادیر بهصورت tuple
print(args[0]) # ایندکس کردن مثل هر tupleای کار میکند
inspect_args(10, 20, 30)
python3 args_type_check.py
Output
<class 'tuple'>
(10, 20, 30)
10
چون args یک Tuple ساده است، میتوانید ایندکسش کنید، برشش بدهید (Slice)، به len() پاسش کنید یا با *args به تابع دیگری Forwardش کنید.
نکته: میتوانید این پارامترها را هر نام معتبر بگذارید. از
*argsو**kwargsبرای توابع همه-منظوره استفاده کنید. از نامهای توصیفی مثل*pricesیا**optionsوقتی مقادیر جمعشده معنای دامنهای روشنی دارند؛ که Signature تابع را خود-مستند میسازد.
*args در عمل
تابعی که همه اعداد دادهشده را ضرب میکند را میتوان با *args نوشت تا برای دو، سه یا بیشتر آرگومان کار کند:
lets_multiply.py
def multiply(*args):
# args یک tuple از همه آرگومانهای positional پاسشده است
z = 1
for num in args:
z *= num
print(z)
multiply(4, 5)
multiply(10, 9)
multiply(2, 3, 4)
multiply(3, 5, 10, 6)
python3 lets_multiply.py
Output
20
90
24
900
بدون *args، افزودن آرگومان سوم نیازمند تغییر Signature تابع و هر Call Site بود. با *args، تابع انعطافپذیر میماند.
استفاده از **kwargs برای پذیرش آرگومانهای Keyword متغیر
**kwargs آرگومانهای Keyword اضافی را در Dict منفردی جمع میکند. هر آرگومان باید با Keyword پاس شود (مثلاً name="Sam").
**kwargs درونزاد چگونه کار میکند
ستاره دوتایی در **kwargs به پایتون میگوید همه آرگومانهای Keyword اضافی را در Dictای بسته و به نام kwargs متصل کند. کلیدها نامهای پارامتری هستند که در محل فراخوانی استفاده شدهاند؛ مقادیر، مقادیر متناظرند. میتوانید این را در زمان اجرا تأیید کنید:
kwargs_type_check.py
def inspect_kwargs(**kwargs):
# نوع و محتوای kwargs را در زمان اجرا نشان بده
print(type(kwargs)) # همیشه <class 'dict'>
print(kwargs) # کل dict
print(kwargs["role"]) # دسترسی مستقیم کلید مثل هر dictای
print(kwargs.get("team", "unset")) # دسترسی امن با پیشفرض
inspect_kwargs(role="admin", name="Sam")
python3 kwargs_type_check.py
Output
<class 'dict'>
{'role': 'admin', 'name': 'Sam'}
admin
unset
چون kwargs یک Dict ساده است، میتوانید از .keys()، .values()، .items()، .get() یا هر متد Dict دیگری رویش استفاده کنید.
**kwargs در عمل
راه سادهای برای بازرسیِ پاسشدهها چاپ کردن kwargs است:
print_kwargs.py
def print_kwargs(**kwargs):
# kwargs یک dict از همه آرگومانهای keyword پاسشده است
print(kwargs)
print_kwargs(kwargs_1="Shark", kwargs_2=4.5, kwargs_3=True)
python3 print_kwargs.py
Output
{'kwargs_1': 'Shark', 'kwargs_2': 4.5, 'kwargs_3': True}
نکته: در پایتون ۳.۷ و بعدتر،
**kwargsترتیب درج را حفظ میکند؛ چون Dict بهطور پیشفرض مرتب است. CPython 3.6 هم ترتیب درج را بهعنوان جزئیات پیادهسازی حفظ میکند؛ اما نسخههای قبلی پایتون ۳ ترتیب Iteration را تضمین نمیکنند.
میتوانید مثل هر Dict دیگری روی جفتهای کلید-مقدار Iterate کنید:
print_values.py
def print_values(**kwargs):
for key, value in kwargs.items():
# روی هر آرگومان keyword iterate کن و نام و مقدارش را چاپ کن
print(f"The value of {key} is {value}")
print_values(my_name="Sammy", your_name="Casey")
python3 print_values.py
Output
The value of my_name is Sammy
The value of your_name is Casey
همچنین میتوانید با .get() و پیشفرض به کلید خاصی از kwargs دسترسی پیدا کنید:
kwargs_access.py
def greet_user(**kwargs):
# با .get() به کلید خاصی امن دسترسی پیدا کن؛ اگر مفقود بود پیشفرض بده
name = kwargs.get("name", "stranger")
role = kwargs.get("role", "user")
print(f"Hello, {name}. Your role is {role}.")
greet_user(name="Sam", role="admin")
greet_user(name="Alex")
python3 kwargs_access.py
Output
Hello, Sam. Your role is admin.
Hello, Alex. Your role is user.
استفاده از .get() با پیشفرض، وقتی Caller آرگومان Keyword اختیاری را حذف میکند از KeyError جلوگیری میکند.
ترکیب *args و **kwargs در یک تابع
مورد رایج دنیای واقعی، قالبساز لاگی است که سطح Severity را الزامی اما هر تعداد بخش پیام و فیلد متادیتای اختیاری را میپذیرد:
log_message.py
def log_message(severity, *args, **kwargs):
# severity: الزامی؛ سطح لاگ را کنترل میکند
# args: هر تعداد بخش پیام، به یک رشته join میشوند
# kwargs: فیلدهای متادیتای اختیاری مانند user، request_id یا service
message = " ".join(str(a) for a in args)
metadata = ", ".join(f"{k}={v}" for k, v in kwargs.items())
print(f"[{severity.upper()}] {message} | {metadata}" if metadata else
f"[{severity.upper()}] {message}")
log_message("info", "User logged in")
log_message("error", "Payment failed", "retry 3 of 3", user="sam", request_id="abc123")
python3 log_message.py
Output
[INFO] User logged in
[ERROR] Payment failed retry 3 of 3 | user=sam, request_id=abc123
فراخوانی log_message فقط با severity و بدون موارد اضافی معتبر است؛ args یک Tuple خالی و kwargs یک Dict خالی خواهد بود.
استفاده از *args و **kwargs هنگام فراخوانی توابع
همان نمادهای * و ** در محل فراخوانی برای Unpack کردن Sequenceها و Mappingها به آرگومانهای Positional و Keyword استفاده میشوند. این معکوسِ جمع کردن است: یک Sequence یا Dict را به چند آرگومان پخش میکنید.
Unpack کردن List با *
پیشگذاشتن * روی Iterableای، عناصرش را بهعنوان آرگومانهای Positional Unpack میکند. مفید وقتی List یا Tupleای دارید و میخواهید هر عنصر را بهعنوان آرگومان جداگانهای پاس بدهید:
unpack_call.py
def greet(first, last, city):
print(f"Hello, {first} {last} from {city}.")
# لیست را برای دو آرگومان اول unpack کن، بعد dict را برای سومی
parts = ["Ada", "Lovelace"]
location = {"city": "London"}
greet(*parts, **location)
python3 unpack_call.py
Output
Hello, Ada Lovelace from London.
اینجا *parts مقدار first و last را تأمین و **location مقدار city را میدهد. میتوانید آرگومانهای Literal را با Unpacking مخلوط کنید:
some_args.py
def some_args(arg_1, arg_2, arg_3):
print("arg_1:", arg_1)
print("arg_2:", arg_2)
print("arg_3:", arg_3)
args = ("Sammy", "Casey", "Alex")
some_args(*args)
python3 some_args.py
Output
arg_1: Sammy
arg_2: Casey
arg_3: Alex
Unpack کردن Dictionary با **
پیشگذاشتن ** روی Mappingای، جفتهای کلید-مقدارش را بهعنوان آرگومانهای Keyword Unpack میکند. کلیدها باید با نامهای پارامتر تابع مطابقت داشته باشند (یا توسط **kwargs پذیرفته شوند):
some_kwargs.py
def some_kwargs(kwarg_1, kwarg_2, kwarg_3):
print("kwarg_1:", kwarg_1)
print("kwarg_2:", kwarg_2)
print("kwarg_3:", kwarg_3)
kwargs = {"kwarg_1": "Val", "kwarg_2": "Harper", "kwarg_3": "Remy"}
some_kwargs(**kwargs)
python3 some_kwargs.py
Output
kwarg_1: Val
kwarg_2: Harper
kwarg_3: Remy
خطاهای رایج Unpacking
دو خطا هنگام Unpacking زیاد ظاهر میشوند.
خطای ۱: آرگومانهای Positional بیش از حد:
unpack_error_positional.py
def add(a, b):
return a + b
values = [1, 2, 3] # یک عنصر اضافه
add(*values)
python3 unpack_error_positional.py
Output
TypeError: add() takes 2 positional arguments but 3 were given
Unpack کردن Sequenceای با عناصر بیشتر از پارامترهای تابع، TypeError میسازد. عناصرتان را بشمارید یا از Slice استفاده کنید: add(*values[:2]).
خطای ۲: کلید Dict با نام پارامتر مطابقت ندارد:
unpack_error_keyword.py
def greet(name, city):
print(f"Hello, {name} from {city}.")
data = {"name": "Sam", "country": "India"} # 'country' پارامتر نیست
greet(**data)
python3 unpack_error_keyword.py
Output
TypeError: greet() got an unexpected keyword argument 'country'
Unpack کردن Dictای که کلیدهایش با نامهای پارامتر تابع مطابقت ندارند، TypeError میسازد. یا کلیدهای Dict را با نامهای پارامتر همراستا کنید؛ یا تضمین کنید تابع **kwargs را برای جذب موارد اضافی میپذیرد.
موارد استفاده عملی
Decoratorای که بدون *args و **kwargs نوشته شده فقط برای توابع با تعداد پارامتر دقیق کار میکند. اعمالش روی هر تابع دیگری میشکندش. سه الگوی زیر نشان میدهند *args و **kwargs چطور این را در رایجترین سناریوهای دنیای واقعی حل میکنند.
الگوی Decorator
Decorator اغلب تابعی را بدون دانستن Signatureاش Wrap میکند. استفاده از *args و **kwargs در Wrapper به او اجازه میدهد هر آرگومانی را به تابع Wrapped-شده Forward کند. مثال: لاگر فراخوانی ساده:
decorator_example.py
import functools
def log_call(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# نام تابع و همه آرگومانها را قبل از فراخوانی لاگ کن
print(f"Calling {func.__name__} with args={args}, kwargs={kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned {result}")
return result
return wrapper
@log_call
def add(x, y):
return x + y
add(3, 7)
python3 decorator_example.py
Output
Calling add with args=(3, 7), kwargs={}
add returned 10
10
Wrapper هر آرگومان Positional و Keywordای را میپذیرد، آنها را به func پاس میدهد و نتیجه را برمیگرداند. همین الگو در Decoratorهای زمانسنجی، Retry و اعتبارسنجی ظاهر میشود.
وراثت کلاس با super()
وقتی Subclassای __init__ را Override میکند و باید Initializer والد را صدا بزند، اغلب همان آرگومانها را Forward میکند. استفاده از *args و **kwargs از تکرار فهرست پارامتر والد اجتناب میکند:
inheritance_example.py
class Animal:
def __init__(self, name, sound):
self.name = name
self.sound = sound
class Dog(Animal):
def __init__(self, *args, breed="unknown", **kwargs):
# همه آرگومانهای positional و keyword را به کلاس والد forward کن
super().__init__(*args, **kwargs)
self.breed = breed
def describe(self):
print(f"{self.name} ({self.breed}) says {self.sound}")
d = Dog("Rex", "woof", breed="Labrador")
d.describe()
python3 inheritance_example.py
Output
Rex (Labrador) says woof
اینجا Dog پارامتر فقط-Keyword مربوط به breed را اضافه و بقیه را به Animal.__init__ Forward میکند. این الگو در فریمورکها و سلسلهمراتب کلاس بزرگ رایج است.
APIهای انعطافپذیر به سبک فریمورک
در فریمورکهایی مثل Flask، Decoratorای Route را ثبت و داده درخواست را به تابع View شما پاس میدهد. ماشینآلات داخلی فریمورک از *args و **kwargs برای Forward کردن پارامترهای URL، رشتههای Query و زمینههای دیگر—بدون دانستن Signature تابع شما از قبل—استفاده میکند. اینجا تصویرسازی مینیمالی از آن الگو هست:
flask_style.py
def route(path):
"""شبیهسازی Decorator فریمورکی که Route یو-آر-ال ثبت میکند."""
def decorator(func):
# Wrapper از *args و **kwargs استفاده میکند تا هر آرگومانی را که
# فریمورک از درخواست جمع میکند به تابع view forward کند
def wrapper(*args, **kwargs):
print(f"Routing {path!r} -> {func.__name__}")
return func(*args, **kwargs)
return wrapper
return decorator
@route("/users/<username>")
def user_profile(username):
return f"Profile page for {username}"
# فریمورک را در حال صدا زدن view با بخش ثبتشده URL شبیهسازی کن
print(user_profile("sam"))
python3 flask_style.py
Output
Routing '/users/<username>' -> user_profile
Profile page for sam
Routeهای واقعی Flask به همین شکل کار میکنند: فریمورک متغیرهای URL را Unpack و آنها را بهعنوان آرگومانهای Keyword به تابع View شما پاس میکند؛ که دلیلش این است که توابع View فقط پارامترهایی را که لازم دارند تعریف میکنند.
حاشیهنویسی نوع با *args و **kwargs
از پایتون ۳.۵ به بعد، میتوانید انواع مقادیر جمعشده توسط *args و **kwargs را Annotate کنید. حاشیهنویسی هر عنصر یا مقدار را توصیف میکند؛ نه خود Tuple یا Dict را:
type_annotations_example.py
def process(*args: int, **kwargs: str) -> None:
# انتظار میرود هر عنصر در args یک int باشد
total = sum(args)
# انتظار میرود هر مقدار در kwargs یک str باشد
labels = ", ".join(kwargs.values())
print(f"Sum: {total}, Labels: {labels}")
process(1, 2, 3, unit="kg", source="sensor")
python3 type_annotations_example.py
Output
Sum: 6, Labels: kg, sensor
پس *args: int یعنی «هر آرگومان Positional باید int باشد» و **kwargs: str یعنی «هر مقدار آرگومان Keyword باید str باشد». Type Checkerها از اینها برای تحلیل ایستا استفاده میکنند؛ مفسر آنها را در زمان اجرا الزامی نمیکند.
برای گرفتن عدم تطابقها بهصورت ایستا، Type Checkery مثل mypy را اجرا کنید:
pip3 install mypy
mypy type_annotations_example.py
اگر بهجای process(1, 2) با process("a", "b") صدا بزنید، mypy خطای Argument of type "str" is not assignable to parameter of type "int" را قبل از اجرای کد گزارش میکند. مفسر همچنان بدون رخداد Exception اجرایش میکند.
ملاحظات کارایی
استفاده از *args و **kwargs هزینه زمان-اجرای کوچکی دارد: پایتون در هر فراخوانی Tuple جدیدی برای آرگومانهای Positional اضافی و Dict جدیدی برای آرگومانهای Keyword اضافی میسازد. میتوانید با timeit اندازهاش بگیرید:
perf_check.py
import timeit
def explicit(a, b, c):
# پارامترهای ثابت: بدون ساخت tuple یا dict
return a + b + c
def with_args(*args):
# *args: پایتون در هر فراخوانی tuple میسازد
return sum(args)
explicit_time = timeit.timeit(lambda: explicit(1, 2, 3), number=1_000_000)
args_time = timeit.timeit(lambda: with_args(1, 2, 3), number=1_000_000)
print(f"explicit: {explicit_time:.3f}s")
print(f"with_args: {args_time:.3f}s")
python3 perf_check.py
Output
explicit: 0.048s
with_args: 0.071s
در یک میلیون فراخوانی، *args حدود ۲۰-۵۰٪ سربار نسبت به پارامترهای صریح در تابع مینیمالی اضافه میکند. برای بیشتر کدهای اپلیکیشن این نامربوط است. برای حلقههای داخلیِ پردازشگر میلیونها آیتم، پارامترهای صریح را ترجیح دهید.
نکته: perf_check.py را روی سیستم خودتان اجرا کنید تا زمانبندی دقیق برای سختافزار و نسخه پایتونتان بگیرید. تفاوت نسبی از مقادیر مطلق مهمتر است.
الگوهای جایگزین
وقتی انعطاف لازم دارید، چند گزینه دارید. جدول زیر آنها را مقایسه میکند:
| رویکرد | چه زمانی استفاده کنیم |
|---|---|
*args / **kwargs | تعداد آرگومان متغیر، Decoratorها، Forwarding به Callable دیگر، APIهای به سبک فریمورک |
| پاس دادن مستقیم List یا Dict | ساختار ثابتِ شناختهشده برای Caller؛ یک شیء میخواهید (مثلاً config: dict) بهجای بسیاری از آرگومانهای Keyword |
| Dataclass یا شیء منفرد | بسیاری از فیلدهای مرتبط با نامها و انواع روشن؛ اعتبارسنجی، پیشفرضها و شیء «options» منفرد میخواهید |
| TypedDict | ساختار شبیه-Keyword با بررسی نوع؛ خوب وقتی مجموعه کلیدها شناختهشده و Type Checking ایستا برای Dict میخواهید |
وقتی API پایدار و تعداد آرگومانها کوچک است، پارامترهای صریح را انتخاب کنید. وقتی API باید آرگومانهای دلخواه را بپذیرد یا Forward کند، از *args/**kwargs استفاده کنید.
سوالات متداول
**تفاوت *args و kwargs در پایتون چیست؟
*args آرگومانهای Positional اضافی را در Tuple جمع میکند. **kwargs آرگومانهای Keyword اضافی را در Dict. آرگومانهای Positional بر اساس موقعیت پاس میشوند (مثلاً f(1, 2))؛ آرگومانهای Keyword بر اساس نام (مثلاً f(a=1, b=2)).
**آیا باید نامشان را *args و kwargs بگذارم؟
نه. نامها قراردادند. ستاره منفرد (*) و ستاره دوتایی (**) الزامیاند؛ میتوانید از *values یا **options یا هر شناسه معتبری استفاده کنید.
آرگومانها در تعریف تابع پایتون باید به چه ترتیبی ظاهر شوند؟
اول پارامترهای Positional استاندارد، بعد *args، بعد پارامترهای فقط-Keyword، بعد **kwargs. نقض این ترتیب SyntaxError میسازد.
**آیا میتوانم *args و kwargs را در همان تابع استفاده کنم؟
بله. *args را قبل از **kwargs استفاده کنید. تابع Tupleای از آرگومانهای Positional اضافی و Dictای از آرگومانهای Keyword اضافی دریافت خواهد کرد.
چطور List یا Dictionary را هنگام فراخوانی تابع Unpack کنم؟
از * برای Unpack کردن Sequence به آرگومانهای Positional استفاده کنید (مثلاً f(*my_list)). از ** برای Unpack کردن Mapping به آرگومانهای Keyword (مثلاً f(**my_dict)). میتوانید هر دو را در یک فراخوانی ترکیب کنید.
**آیا میتوانم *args و kwargs را Type-Annotate کنم؟
بله. در پایتون ۳.۵ و بعدتر میتوانید *args: int و **kwargs: str بنویسید. حاشیهنویسی به هر عنصر یا مقدار اعمال میشود؛ نه به Tuple یا Dict. Type Checkerها از اینها برای تحلیل ایستا استفاده میکنند.
**آیا هزینههای کارایی برای استفاده از *args و kwargs وجود دارد؟
بله؛ اما معمولاً کوچک. هر فراخوانی Tuple جدیدی برای *args و Dict جدیدی برای **kwargs میسازد. در مسیرهای داغ یا حجم فراخوانی خیلی بالا، اگر مجموعه آرگومان ثابت است پارامترهای صریح را ترجیح دهید.
***args و kwargs در وراثت کلاس چگونه استفاده میشوند؟
Subclassها اغلب در __init__ (یا متدهای دیگر) از *args و **kwargs استفاده و آنها را به super().__init__(*args, **kwargs) پاس میدهند تا والد همان آرگومانها را دریافت کند؛ بدون اینکه Subclass هر پارامتر والد را تعریف کند. Subclass میتواند پارامترهای خودش را قبل یا بعد از Forwarding اضافه کند.
نتیجهگیری
*args و **kwargs به شما اجازه میدهند توابعی تعریف کنید که تعداد متغیری از آرگومانهای Positional و Keyword را میپذیرند؛ و هنگام فراخوانی توابع، Sequenceها و Mappingها را Unpack کنید. از آنها وقتی استفاده کنید که Signatureهای انعطافپذیر، Decoratorها یا Forwarding (مثلاً به super() یا APIهای فریمورک) لازم دارید. ترتیب آرگومانها را درست نگه دارید؛ و وقتی API ثابت است و کارایی مهم، پارامترهای صریح را ترجیح دهید.




