1️⃣ Why *args and **kwargs exist

Problem:
Normally a Python function expects a fixed number of arguments.

Solution:

  • *args Arguments
    • Collects extra positional arguments
    • Store as touple
  • **kwargs Keyword Arguments
    • Collects extra keyword arguments
    • Store as dictionary
    • positional → *args → keyword → **kwargs
      • ex. def func(a, b, *args, c=10, d=20, **kwargs):

2️⃣ *args (positional arguments)

def show(*args):
	print(type(args))  # <class 'tuple'>
    print(args) # (1, 2, 3)
 
show(1, 2, 3)
 
 
### 🔹 Mixing normal args and `*args`
def demo(a, b, *args):
    print(a) # 1
    print(b) # 2
    print(args) # (3, 4, 5)
 
demo(1, 2, 3, 4, 5)

3️⃣ **kwargs (keyword arguments)

def show(**kwargs):
	print(type(kwargs)) # <class 'dict'>
    print(kwargs)
 
show(a=1, b=2) # {'a': 1, 'b': 2}
 
 
# positional → *args → keyword → **kwargs
def mix(a, b, *args, x = None , **kwargs):
    print(a, b) # 1 2
    print(args) # (3, 4)
    print(x) # 20
    print(kwargs) # {'x': 10, 'y': 20}
 
 
mix(1, 2, 3, 4, x=10, y=20, z=30)

4️⃣ Unpacking arguments

Function Call Unpacking

DataCallResult
extras = (1,2,3)func(*extras)same as func(1,2,3)
info = {'a':1,'b':2}func(**info)same as func(a=1,b=2)
# Unpacking list/tuple using 
nums = [1, 2, 3]
print(*nums) # 1 2 3
 
 
# Unpacking dict
def show(a, b):
    print(a, b) # 1 2
 
data = {"a": 1, "b": 2}
show(**data)
 
 
# Forwarding arguments (very important)
# Used heavily in decorators, wrappers, frameworks.
 
def wrapper(*args, **kwargs):
    return original(*args, **kwargs)