پایتون

نحوه استفاده از *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
ساختار دادهTupleDict
نوع آرگومانPositionalKeyword
روش Iterationfor val in args`for k, v in kwargs.items()“
Keyword در فراخوانی لازم است؟خیربله
هدف Type Annotationهر عنصرهر مقدار

قوانین ترتیب آرگومان‌ها

آرگومان‌ها در تعریف تابع باید به ترتیب سخت‌گیرانه‌ای ظاهر شوند. جدول زیر ترتیب قانونی را نشان می‌دهد؛ نقض آن SyntaxError raised می‌کند:

موقعیتنوعمثال
۱Positional استانداردarg_1، arg_2
۲Positional متغیر*args
۳فقط-Keywordkw_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 ثابت است و کارایی مهم، پارامترهای صریح را ترجیح دهید.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا