args* و kwargs** در پایتون

args* به ما این امکان رو میده که به جای تعداد ثابتی ورودی یه تابع بتونه تعداد متغیری ورودی بگیره. برای مثال تابع زیر رو در نظر بگیرید:

def my_sum(a, b):
    return a + b

my_sum(2, 3) # 5

این تابع دو تا عدد رو باهم جمع می‌کنه و همیشه دو تا ورودی می‌گیره و اگه بیشتر یا کمتر ورودی بهش بدیم با خطا مواجه می‌شیم.
فرض کنید به جای دو عدد میخوایم چندتا عدد (که ممکنه در هر بار فراخوانی تابع تعدادشون فرق کنه) رو با هم جمع کنیم. یه راه اینه که تابعمون یه ورودی list بگیره و اعداد توی لیست رو با هم جمع کنه. برای مثال:

def sum_list(nums):
    sum = 0
    for num in nums:
        sum += num
    return sum

sum_list([2, 3]) # 5
sum_list([2, 3, 5, 8]) # 18

اما راه دیگه اینه که به جای قرار دادن عددهامون توی یه لیست، اونارو مستقیم و به شکل آرگومان به تابعمون بدیم:

def my_sum(*nums):
    sum = 0
    for num in nums:
        sum += num
    return sum
    
my_sum(2, 3) # 5
my_sum(2, 3, 5, 8) # 18

چه اتفاقی افتاد؟!
هر تابع می‌تونه یک ورودی بگیره که قبل از اسمش یه قرار داره و می‌تونه هر اسمی داشته باشه مثل: args، argv و … و تمام آرگومان‌های اضافی که به تابع داده میشه رو برای ما نگه میداره. (که جنسش از tuple هست).
بیاین یه مثال دیگه ببینیم:

def my_sum(a, *nums):
    sum = 0
    for num in nums:
        sum += num
    return sum * a

my_sum(10, 2, 3) # 50
my_sum(10, 2, 10, 2) # 140

اولین ورودی تابع بالا یه عدده که در مجموع تمام عدد‌های ورودی بعدی ضرب میشه. اولین ورودی تابع اجباریه، یعنی نمی‌تونیم هیچ ورودی به تابع ندیم. اما بقیه‌ی ورودی‌ها اختیاری هستن و به هر تعداد دلخواه می‌تونیم ورودی به تابع بدیم.

اما حالا فرض کنید که ما یه متغیر از جنس list داریم که تعدادی عدد توش ذخیره شده و می‌خوایم مجموع این اعداد را محاسبه کنیم. اگه اون متغیر را مستقیم به تابعمون بدیم درست کار نخواهد کرد، چون یه متغیر لیست یه دونه ورودی حساب میشه و تابع ما با لیست مثل یه عدد رفتار خواهد کرد! پس چاره چیست؟
چاره است!

def my_sum(a, *nums):
    sum = 0
    for num in nums:
        sum += num
    return sum * a

lst1 = [1, 2, 3, 4]
lst2 = [10, 11, 12, 13]

my_sum(10, *lst1) # 100
my_sum(10, *lst2) # 460

my_sum(10, *lst1, *lst2) # 560

اگه زمان فراخوانی تابع، قبل از یه متغیر از نوع list یا tuple یه دونه ستاره (*) قرار بدیم باعث میشه عناصر داخل اون لیست یا تاپل به عنوان ورودی‌های تابع در نظر گرفته بشن! و حتی می‌تونیم چند تا از لیست‌ یا تاپل‌های ستاره‌دار رو به ورودی تابع بدیم و همه‌ی عناصر داخل اونها از طریق یه پارامتر شبیه به args* در داخل تابع قابل دسترسی خواهند بود.

kwargs** هم مشابه args* عمل می‌کنه با این تفاوت که آرگومان‌های با کلمه‌کلیدی رو ذخیره می‌کنه.
مثال زیر را در نظر بگیرید:

def show_person_info(**kwargs):
    for key, value in kwargs.items():
        print('{} = {}'.format(key, value))
 
show_person_info(name='Hamidreza',
     family='Mahdavipanah',
     age='22') 

# خروجی:
#
# family = Mahdavipanah
# age = 22
# name = Hamidreza

تابع بالا تعداد دلخواهی از ورودی‌های با اسم رو دریافت می‌کنه و دونه دونه هرکدومشونو همراه با اسم ورودی و مقدار داخلش چاپ میکنه.
نوع داده‌ی kwargs از جنس dictionary است.
kwargs** می‌تونه هر نام دیگه‌ای داشته باشه، مهم ** داخل اسمشه.
kwargs**  باید تو لیست پارامتر‌های تابع آخرین پارامتر باشه.

تبدیل دیکشنری به ورودی‌های با کلمه‌کلیدی:

def show_person_info(**kwargs):
    for key, value in kwargs.items():
        print('{} = {}'.format(key, value))
        
dct = {
    'name': 'Hamidreza',
    'family': 'Mahdavipanah',
    'blog': 'mahdavipanah.com/blog',
    'github': 'github.com/mahdavipanah'
}        
 
show_person_info(**dct) 

# Output:
#
# name = Hamidreza
# blog = mahdavipanah.com/blog
# github = github.com/mahdavipanah
# family = Mahdavipanah

پیاده‌سازی عملکردی شبیه به تابع join در رشته‌ها با استفاده از args*:

class String(str):
   def my_join(self, *args):
      return self.join(args)

s = String('')

# Python style:
s.join(['Hello ', 'world', '!'])
# My style:
s.my_join('Hello ', 'World', '!')

# Both return: 'Hello world!'

# Join list of strings using my style of join
l = ['Hello ', 'world', '!']
s.my_join(*l)
# returns: 'Hello world!'

و اما یه چیز دیگه! تمام آرگون‌هایی که بعد از args* قرار می‌گیرن باید از نوع ورودی‌های با اسم‌ باشن. حالا اگه خواستیم تابعمون args* نگیره اما از یه جایی به بعد تمام ورودی‌هاش با اسم باشن باید چیکار کنیم؟
چاره یه علامت ستاره‌ی خالیه:

def my_func(a, b, c, d, *, first_kw_arg, second_kw_arg=True):
    pass

my_func(1, 2, 3, 4)
# Error: missing 1 required keyword-only argument: 'first_kw_arg'

my_func(1, 2, 3, first_kw_arg=4)
# Works fine!

my_func(1, 2, 3, first_kw_arg=4, second_kw_arg=False)
# Works fine!

 

پاسخ دهید

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