One of Python’s most flexible and readable features is its support for variable-length arguments using *args and **kwargs.
But if you’ve ever wondered:
- What do
*argsand**kwargsactually mean? - How do they work under the hood?
- When should you use them — and when shouldn’t you?
You’re in the right place. This guide explains it all, with clear examples, visual breakdowns, and pro tips for interviews and real-world use.
🧠 What Are *args and **kwargs?
| Syntax | Name | Accepts |
|---|---|---|
*args | Positional arguments | A tuple of unnamed values |
**kwargs | Keyword arguments | A dictionary of named values |
✅ Using *args: Accepting Multiple Positional Arguments
pythonCopyEditdef add(*args):
return sum(args)
print(add(1, 2, 3)) # ➜ 6
print(add(10, 20)) # ➜ 30
What’s happening:
argsis a tuple:(1, 2, 3)- You can pass any number of values
- You don’t have to name them
✅ Using **kwargs: Accepting Multiple Keyword Arguments
greet(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
greet(name="Alice", age=30)
What’s happening:
kwargsis a dictionary:{'name': 'Alice', 'age': 30}- You can pass any named (key=value) arguments
📦 Combining *args and **kwargs
Yes, you can use both — and this is where things get powerful:
def log(level, *args, **kwargs):
print(f"[{level}]")
print("ARGS:", args)
print("KWARGS:", kwargs)
log("INFO", 1, 2, a=10, b=20)
✅ Output:
[INFO]
ARGS: (1, 2)
KWARGS: {'a': 10, 'b': 20}
🧠 Order matters: *args must come before **kwargs.
🧩 Unpacking with * and ** When Calling Functions
These stars also work in reverse, to unpack data into arguments:
nums = [1, 2, 3]
print(add(*nums)) # ➜ same as add(1, 2, 3)
info = {"name": "Bob", "age": 25}
greet(**info) # ➜ same as greet(name="Bob", age=25)
🧠 Visual Breakdown
def example(fixed, *args, **kwargs):
...
Call:
pythonCopyEditexample("Hi", 1, 2, 3, x=10, y=20)
Internally:
fixed = "Hi"
args = (1, 2, 3)
kwargs = {'x': 10, 'y': 20}
💼 Real-World Use Cases
| Use Case | Why *args / **kwargs Help |
|---|---|
| Logging / debugging tools | Capture any input flexibly |
| API wrapper functions | Forward unknown inputs |
| Class inheritance / override | Accept parent class params |
| Decorators | Work with arbitrary functions |
| Config loading | Accept unknown keyword config |
💡 Interview Questions You Might Hear
❓ “What is the difference between *args and **kwargs?”
✅ *args captures extra positional arguments as a tuple.
✅ **kwargs captures extra keyword arguments as a dictionary.
❓ “Can I use both at once?”
Yes, in this order:
def my_func(fixed, *args, **kwargs): ...
❓ “Can I forward arguments to another function?”
Yes!
def wrapper(*args, **kwargs):
return original_func(*args, **kwargs)
⚠️ Common Mistakes
- ❌ Using
*argsafter**kwargs— order matters! - ❌ Assuming
argsis a list — it’s actually a tuple - ❌ Confusing unpacking syntax (
*/**) for passing vs receiving
✅ Summary
| Concept | Meaning |
|---|---|
*args | Accepts extra positional arguments (tuple) |
**kwargs | Accepts extra keyword arguments (dict) |
| Unpacking | Use * and ** when calling functions |
| Order | Always: def func(fixed, *args, **kwargs) |
🏁 Final Thoughts
Python gives you a powerful, elegant way to write flexible functions that can accept any number of arguments. Whether you’re building a logging utility, designing APIs, or writing decorators — mastering *args and **kwargs is essential.
So next time you see a function definition full of stars, you’ll know:
“This function is ready for anything.”

Leave a Reply