(aka: The Art of Talking With Your Functions Without Losing Your Mind)
Functions are like little wizards in Python. You hand them some ingredients, they stir their magical cauldron, and-poof-you get a result. But here’s the catch: if you don’t know how to hand over those ingredients correctly, your wizard might either mess up the potion or throw a TypeError
tantrum.
So today, we’re going on a deep dive into function parameters: arguments vs parameters, positional vs keyword arguments, the mysteries of *args
and **kwargs
, unpacking secrets, and the sneaky pitfalls of default parameters. By the end, you’ll not only understand them, you’ll be able to explain them like an absolute genius.
Arguments vs Parameters, The First Misunderstanding
Let’s clear this up before we go too far:
- Parameters are the names you define in the function.
- Arguments are the actual values you pass when you call the function.
Think of it like this:
def make_pizza(size, topping): # parameters
print(f"Making a {size}-inch pizza with {topping}.")
make_pizza(12, "pepperoni") # arguments
👉 Parameters = variables waiting for data.
👉 Arguments = the data you actually send.
Simple, but critical.
Positional vs Keyword Arguments
Python lets you call functions in two ways:
- Positional arguments order matters:
make_pizza(16, "mushrooms") # size=16, topping="mushrooms"
- Keyword arguments order doesn’t matter:
make_pizza(topping="olives", size=14)
If you mix them, positional always goes first. Otherwise, Python gets confused:
make_pizza(12, topping="onions") # ✅ fine
make_pizza(size=12, "onions") # ❌ SyntaxError
The Magic of Unpacking
Unpacking is like giving Python a backpack of values and saying: “Here, open this and figure it out.”
Iterable unpacking with *
:
def greet(a, b, c):
print(a, b, c)
values = [1, 2, 3]
greet(*values) # same as greet(1, 2, 3)
Dictionary unpacking with **
:
def introduce(name, age):
print(f"My name is {name}, I'm {age} years old.")
person = {"name": "Alice", "age": 25}
introduce(**person) # same as introduce(name="Alice", age=25)
This is insanely powerful when you don’t know ahead of time how many values you’ll deal with.
Enter *args
The Collector of Extras
Sometimes you don’t know how many arguments someone will pass. That’s where *args
comes in.
def party(organizer, *guests):
print(f"Organizer: {organizer}")
print("Guests:", guests)
party("Alice", "Bob", "Charlie", "Dana")
Output:
Organizer: Alice
Guests: ('Bob', 'Charlie', 'Dana')
Notice that *args
packs all extra positional arguments into a tuple.
Think of it as: “whatever’s left over, put it in a bag.”
Enter **kwargs
The Dictionary of Chaos
And then comes the sibling: **kwargs
.
It gathers all extra keyword arguments into a dictionary.
def profile(name, **details):
print(f"Name: {name}")
for key, value in details.items():
print(f"{key}: {value}")
profile("Alice", age=25, city="London", hobby="chess")
Output:
Name: Alice
age: 25
city: London
hobby: chess
Boom! Suddenly, your function is infinitely flexible.
Combining *args
and **kwargs
The ultimate weapon:
def everything(required, *args, **kwargs):
print("Required:", required)
print("Args:", args)
print("Kwargs:", kwargs)
everything("Hello", 1, 2, 3, a=10, b=20)
Output:
Required: Hello
Args: (1, 2, 3)
Kwargs: {'a': 10, 'b': 20}
Golden rule of parameter order:
- Normal parameters
*args
- Keyword-only parameters
**kwargs
If you mess this up, Python will shout at you.
Extended Unpacking, Next-Level Wizardry
Python even lets you “spread” collections inside assignments:
numbers = [1, 2, 3, 4, 5]
a, *middle, b = numbers
print(a) # 1
print(middle) # [2, 3, 4]
print(b) # 5
This is brilliant for when you need the “edges” of a list but don’t care about the middle (or vice versa).
Parameter Defaults, Beware of Mutables!
Here’s a classic Python “gotcha”:
def add_item(item, bucket=[]): # 🚨 Danger!
bucket.append(item)
return bucket
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] <-- surprise!
Why? Because the default list is created once, not each time. So it keeps growing.
The safe way:
def add_item(item, bucket=None):
if bucket is None:
bucket = []
bucket.append(item)
return bucket
Always be careful with mutable default arguments (lists, dicts, sets).
Putting It All Together
You now have the full toolkit:
- Parameters vs arguments ✅
- Positional & keyword arguments ✅
- Unpacking iterables and dictionaries ✅
-
*args
and**kwargs
mastery ✅ - Extended unpacking ✅
- Default parameter pitfalls ✅
With these in your arsenal, you’re no longer just “using functions” you’re wielding them like a sorcerer.
The next time someone complains about messy function calls, you can smile, sip your coffee, and whisper: “Have you tried *args
and `kwargs`?”
Good post man. Continue.
By Sami
sami2012ammar@gmail.com