5 Ways You’re Misusing *args and **kwargs in Python
Master Python’s most misunderstood feature — avoid the silent bugs, confusing APIs, and lost readability that come with misusing *args and…

You’re probably using *args
and **kwargs
because you’ve seen others do it. But do you really know what they’re doing behind the scenes?
5 Ways You’re Misusing *args
and **kwargs
in Python
Master Python’s most misunderstood feature — avoid the silent bugs, confusing APIs, and lost readability that come with misusing *args
and **kwargs
.
Python gives us a lot of flexibility, and one of the most powerful tools in our arsenal is *args
and **kwargs
.
These special symbols allow us to write functions that accept a variable number of arguments — a dream for any developer building reusable components or dynamic interfaces.
But with great power comes great potential for misuse.
If you’ve been coding in Python for a while, chances are you’ve used *args
and **kwargs
. But are you using them the right way?
In this article, we’ll explore 5 common ways developers misuse *args
and **kwargs
, and how to turn those mistakes into clean, Pythonic code.
1. Using *args
and **kwargs
Without Documentation
Let’s face it — when you see a function like this:
def do_something(*args, **kwargs):
pass
…you have no clue what to pass in.
The Mistake:
Using *args
and **kwargs
without documenting or clearly specifying what the function expects is a recipe for confusion. It’s great for flexibility, terrible for readability.
The Fix:
If you use them, always document expected parameters in the function docstring or explicitly unpack the ones you need.
def send_email(*args, **kwargs):
"""
Sends an email.
Keyword arguments:
subject -- Subject of the email
to -- Recipient email address
body -- Body content of the email
"""
subject = kwargs.get('subject')
to = kwargs.get('to')
body = kwargs.get('body')
# ...send email
Better yet? Use keyword-only arguments.
def send_email(*, subject, to, body):
# No need for **kwargs if arguments are known
2. Overusing *args
and **kwargs
When Explicit Parameters Work Better
The Mistake:
Trying to be too clever by using *args
and **kwargs
everywhere, even when the function parameters are known and fixed.
def add(*args):
return sum(args)
Sure, it works. But it’s not always the best choice, especially when your function expects exactly two numbers.
The Fix:
Be explicit when you can. Use *args
only when the number of inputs genuinely varies.
def add(a, b):
return a + b
Or, if you do want to support variable input:
def add(*args):
if not args:
raise ValueError("At least one number is required")
return sum(args)
3. Modifying *args
and **kwargs
Directly
The Mistake:
Trying to modify args
or kwargs
directly as if they were regular variables.
def modify(*args):
args[0] = 10 # ❌ This will throw a TypeError
Why? Because args
is a tuple — it’s immutable.
The Fix:
If you need to modify it, convert it to a list first.
def modify(*args):
args = list(args)
args[0] = 10
return args
For kwargs
, which is a dictionary, modification is allowed — but proceed with caution. Don't mutate it unless absolutely necessary.
4. Forgetting to Forward *args
and **kwargs
in Wrappers
If you’ve ever written a decorator or a wrapper function and it mysteriously broke, this might be why.
The Mistake:
Forgetting to pass *args
and **kwargs
to the wrapped function.
def log_decorator(func):
def wrapper():
print("Calling function...")
return func() # ❌ Breaks if func expects arguments
return wrapper
The Fix:
Always forward the arguments properly.
def log_decorator(func):
def wrapper(*args, **kwargs):
print("Calling function...")
return func(*args, **kwargs)
return wrapper
Even better: use functools.wraps
to preserve metadata.
5. Confusing *args
and **kwargs
in Function Calls
The Mistake:
Using *args
when the function expects keyword arguments — or vice versa.
def greet(name, age):
print(f"Hello, {name}. You are {age}.")
args = ("Aashish", 30)
kwargs = {"name": "Aashish", "age": 25}
greet(args) # ❌ TypeError
greet(**args) # ❌ TypeError again
The Fix:
Use *
when unpacking positional arguments and **
when unpacking keyword arguments — don’t mix them.
greet(*args) # ✅ Works
greet(**kwargs) # ✅ Works
Bonus Tip: Combine Explicit Parameters with *args
/**kwargs
Don’t feel like it’s all or nothing.
You can combine required parameters, flexible arguments, and keyword-only arguments elegantly:
def process_request(user_id, *args, timeout=30, **kwargs):
print(user_id, args, timeout, kwargs)
This gives you a good mix of strictness and flexibility.
Final Thoughts
*args
and **kwargs
are beautiful features that make Python incredibly dynamic and flexible. But misuse them — and you’ll end up with code that’s confusing, buggy, or hard to maintain.
Use them wisely. Be intentional. Be explicit where it matters. And always prioritize clarity over cleverness.
Enjoyed this article?
Follow me for more Python tricks, debugging tips, and clean code practices!
Let’s write better Python together.
