1.2.29 · D4 · HinglishIntroduction to Programming (Python)

Exercises` - args` and ` - kwargs` — flexible argument passing

2,600 words12 min read↑ Read in English

1.2.29 · D4 · Coding › Introduction to Programming (Python) › [[1.2.29 - args and - kwargs — flexible argument passing| - args and - kwargs — flexible argument passing]]

Ek quick refresher — bas ek rule jo tumhe sach mein yaad rakhna hai:

Figure — ` - args` and ` - kwargs` — flexible argument passing

Level 1 — Recognition

Yahan tumhe bas decide karna hai ki ek naam kis type ka value hold karta hai aur ek value kis box mein jaati hai.

Exercise 1.1

args ki exact value aur Python type batao.

def f(*args):
    return args
print(f(7, 8, 9))
Recall Solution 1.1

Definition mein star saare positional arguments ko ek tuple mein collect karta hai. Teen values aati hain, toh:

  • Output: (7, 8, 9)
  • Type: ek tuple (round brackets, immutable). Humne kya kiya: star ko "leftovers gather karo" ki tarah padha. Kyun: *args se pehle koi named parameter nahi hai, toh har value ek leftover hai.

Exercise 1.2

Yahan kwargs kya hai — value aur type?

def g(**kwargs):
    return kwargs
print(g(x=1, y=2))
Recall Solution 1.2

** key=value pairs ko ek dict mein collect karta hai:

  • Output: {'x': 1, 'y': 2}
  • Type: ek dict (curly braces, key→value).

Exercise 1.3

In mein se kaun se legal function headers hain? Har ek ko mark karo.

# A
def a(*args, **kwargs): pass
# B
def b(**kwargs, *args): pass
# C
def c(x, *args): pass
# D
def d(**kwargs): pass
Recall Solution 1.3
  • A — legal. *args ka **kwargs se pehle aana correct order hai.
  • B — SyntaxError. **kwargs last parameter hona chahiye; catch-all dict ke baad kuch nahi aa sakta.
  • C — legal. Ek named positional ke baad *args.
  • D — legal. **kwargs akela bhi theek hai. B kyun fail hota hai: jab ek aisa box ho jo saare remaining keywords absorb kar le, toh uske baad kuch bhi catch karne ke liye kuch bachta hi nahi.

Level 2 — Application

Ab tum kai arguments ko correct box mein route karte ho aur ek result compute karte ho.

Exercise 2.1

Poora return value predict karo.

def f(a, *args, **kwargs):
    return a, args, kwargs
print(f(1, 2, 3, x=9, y=10))
Recall Solution 2.1

Slots ko left se right fill karo:

  • a pehla positional leta hai → 1.
  • *args baaki positionals sweep karta hai → (2, 3).
  • **kwargs aise keywords sweep karta hai jinका koi named slot nahi → {'x': 9, 'y': 10}.
  • Output: (1, (2, 3), {'x': 9, 'y': 10})

Exercise 2.2

Ek function product likho jo kisi bhi number of arguments ko multiply kare, aur koi argument na ho toh 1 return kare. Phir product(2, 3, 4) aur product() do.

Recall Solution 2.2
def product(*nums):
    total = 1
    for x in nums:
        total *= x
    return total
  • product(2, 3, 4)2*3*4 = 24.
  • product()nums empty tuple () hai, loop kabhi nahi chalta, toh starting total = 1 return hota hai. 1 se kyun shuru karein? 1 multiplicative identity hai — isse multiply karna kuch nahi badalta, toh ye correct "empty product" hai, bilkul waise jaise 0 correct empty sum hai.

Exercise 2.3

Call-site unpacking use karo taaki add ek list se aur ek dict se kaam kare.

def add(a, b, c):
    return a + b + c
 
nums = [5, 6, 7]
opts = {'a': 10, 'b': 20, 'c': 30}
# fill in with * and **
Recall Solution 2.3
add(*nums)   # spreads to add(5, 6, 7)  -> 18
add(**opts)  # spreads to add(a=10, b=20, c=30) -> 60
  • add(*nums)18.
  • add(**opts)60. Yahan star kya karta hai (opposite direction): ek call mein ye ek collection ko wapas alag-alag arguments mein spread karta hai, bilkul mirror image of how it gathered them in a definition.

Level 3 — Analysis

Yahan tumhe reason karna hoga ki kyun ek call succeed, fail, ya override hoti hai.

Exercise 3.1

Explain karo kyun pehli call kaam karti hai aur doosri error raise karti hai.

def f(a, b):
    return a + b
 
f(*[1, 2])      # call 1
f(*[1, 2, 3])   # call 2
Recall Solution 3.1
  • Call 1: *[1, 2] exactly do positionals mein spread hota hai → f(1, 2)3. ✅
  • Call 2: *[1, 2, 3] teen positionals mein spread hota hai, lekin f ke paas sirf a aur b slots hain. Extra 3 catch karne ke liye koi *args nahi hai, toh Python TypeError: f() takes 2 positional arguments but 3 were given raise karta hai. Analysis: unpacking count ko maaf nahi karta — ye bas itne arguments hand over karta hai. Receiving header ko unhe hold karne mein capable hona chahiye.

Exercise 3.2

final kya hai aur kyun?

defaults = {'color': 'red', 'size': 'M', 'qty': 1}
user     = {'size': 'L', 'qty': 3}
final = {**defaults, **user}
Recall Solution 3.2
  • final = {'color': 'red', 'size': 'L', 'qty': 3}. Kyun: {**a, **b} pehle a ko pour karta hai, phir b ko. Jab ek key dono mein hoti hai, toh baad wali value jeet jaati hai kyunki wo baad mein same slot mein likhi jaati hai. Toh user ka size='L' aur qty=3 defaults ko override karta hai, jabki color (sirf defaults mein) untouched rehta hai.

Exercise 3.3

Dhyan se trace karo. Ye kya print karta hai?

def f(a, b=100, *args):
    return a, b, args
print(f(1))
print(f(1, 2, 3, 4))
Recall Solution 3.3
  • f(1): a=1, b apna default 100 use karta hai, args=(). Output: (1, 100, ()).
  • f(1, 2, 3, 4): a=1, b=2 (default override hota hai), phir leftovers 3, 4 args=(3, 4) mein sweep hote hain. Output: (1, 2, (3, 4)). Analysis: ek default parameter pehle next positional se fill hota hai; sirf baad mein jab har named slot satisfy ho jaata hai tab *args collect karna shuru karta hai.

Level 4 — Synthesis

Ab tum reusable tools banate ho jo aisi arguments forward karte hain jo unhe samajh nahi aate.

Exercise 4.1

call_twice(func, *args, **kwargs) likho jo func ko same arguments ke saath do baar call kare aur dono results ek tuple mein return kare. pow ke saath test karo.

Recall Solution 4.1
def call_twice(func, *args, **kwargs):
    return func(*args, **kwargs), func(*args, **kwargs)
 
call_twice(pow, 2, 3)   # (8, 8)
  • Output: (8, 8). Ye kisi bhi function ke liye kyun kaam karta hai: func ke baad jo bhi aata hai use hum *args, **kwargs se catch karte hain, phir func(*args, **kwargs) se wapas spread karte hain. call_twice ko kabhi pow ki real signature jaanne ki zaroorat nahi.

Exercise 4.2

Ek debug wrapper (ek mini-decorator) likho jo print kare ki function ko kis arguments ke saath call kiya gaya, phir uska result unchanged return kare. Ek chhote se mul pe test karo.

Recall Solution 4.2
def debug(func):
    def wrapper(*args, **kwargs):
        print("call", func.__name__, args, kwargs)
        result = func(*args, **kwargs)
        print("->", result)
        return result
    return wrapper
 
@debug
def mul(a, b):
    return a * b
 
mul(6, 7)   # prints, then returns 42
  • mul(6, 7) ka return value → 42.
  • Print hoga: call mul (6, 7) {} phir -> 42. *args, **kwargs kyun zaroori hai: wrapper ko koi bhi signature accept karni chahiye jo decorated function ki ho sakti hai, toh wo sab kuch generically catch karta hai aur faithfully forward karta hai.

Exercise 4.3

merge_settings(base, **overrides) likho jo ek nayi dict return kare: base jisme overrides top pe applied hon (base ko mutate kiye bina). Ise test karo.

Recall Solution 4.3
def merge_settings(base, **overrides):
    return {**base, **overrides}
 
cfg = {'debug': False, 'level': 1}
merge_settings(cfg, level=5, verbose=True)
# {'debug': False, 'level': 5, 'verbose': True}
  • Output: {'debug': False, 'level': 5, 'verbose': True}.
  • cfg unchanged rehta hai (abhi bhi {'debug': False, 'level': 1}) kyunki {**base, ...} ek nayi dict banata hai. Kyun: **overrides caller keywords ko ek dict mein catch karta hai, aur merge literal overrides ko last mein likhta hai, toh wo jeet jaate hain — exactly "user beats defaults" pattern.

Level 5 — Mastery

Mixed, tricky rules ke under poora reasoning.

Exercise 5.1

Output predict karo. Keyword-only parameter sep ko dhyan se dekho.

def join_all(*parts, sep="-"):
    return sep.join(parts)
 
print(join_all("a", "b", "c"))
print(join_all("a", "b", "c", sep="/"))
Recall Solution 5.1

sep *args ke baad baithta hai, toh ye keyword-only ban jaata hai — tum isse sirf naam se set kar sakte ho.

  • join_all("a", "b", "c"): parts=("a","b","c"), sep apna default "-" rakhta hai → "a-b-c".
  • join_all("a", "b", "c", sep="/"): same parts, sep="/""a/b/c". Kyun keyword-only: kyunki *parts laalach se har positional kha jaata hai, sep tak pahunchne ka ek hi tarika hai — use naam se bulao — ye ek deliberate, useful design hai.

Exercise 5.2

Ye error raise karta hai. Exactly batao kaun si aur kyun, phir ise fix karo taaki x keyword-only ban jaaye.

def f(*args, x):
    return args, x
 
f(1, 2, 3)
Recall Solution 5.2
  • f(1, 2, 3)TypeError: f() missing 1 required keyword-only argument: 'x'. Kyun: *args ke baad x keyword-only hai aur uska koi default nahi, toh ye naam se required hai. Teen positionals args=(1,2,3) fill karte hain lekin koi x=... supply nahi karta.
  • Fix (ise optional banao):
def f(*args, x=0):
    return args, x
f(1, 2, 3)         # ((1, 2, 3), 0)
f(1, 2, x=9)       # ((1, 2), 9)
  • f(1, 2, 3)((1, 2, 3), 0); f(1, 2, x=9)((1, 2), 9).

Exercise 5.3

Full trace — sab kuch combine karo. Program kya print karta hai?

def report(title, *rows, sep=" | ", **meta):
    line = sep.join(str(r) for r in rows)
    return title, line, meta
 
print(report("Scores", 10, 20, 30, sep=", ", author="Ravi", year=2024))
Recall Solution 5.3

Har argument ko route karo:

  • title = "Scores" (pehla positional).
  • *rows remaining positionals sweep karta hai → (10, 20, 30).
  • sep explicitly naam se diya gaya → ", " (keyword-only).
  • **meta leftover keywords catch karta hai → {'author': 'Ravi', 'year': 2024}.
  • line = "10, 20, 30".
  • Output: ('Scores', '10, 20, 30', {'author': 'Ravi', 'year': 2024}). Ye poori picture kyun hai: ye poora legal order exercise karta hai — positional, *args, keyword-only, **kwargs — sab ek hi call mein. Neeche figure dekho jo routing trace karta hai.
Figure — ` - args` and ` - kwargs` — flexible argument passing

Exercise 5.4

{**a, **b} conflict + unpacking count. Dono lines predict karo.

a = {'p': 1, 'q': 2}
b = {'q': 9, 'r': 3}
print({**a, **b})
 
def g(p, q, r):
    return p + q + r
print(g(**{**a, **b}))
Recall Solution 5.4
  • Merge: pehle a, phir b; conflicting qb jeet jaata hai. Result {'p': 1, 'q': 9, 'r': 3}.
  • g(**{...}) us dict ko g(p=1, q=9, r=3) ki tarah spread karta hai → 1 + 9 + 3 = 13.
  • Outputs: {'p': 1, 'q': 9, 'r': 3} phir 13.


Connections

  • Functions and Parameters — wo base jis par har exercise banti hai.
  • Default Arguments — L3 aur L5 mein use hua.
  • Tuples*args kya yield karta hai.
  • Dictionaries**kwargs kya yield karta hai; L3/L5 mein merge rules.
  • Decorators — L4 wrapper pattern.
  • Unpacking Assignment — assignments mein same star idea.