Python Default Parameters
Table of Contents + −
In the last lesson you learned about Return Values, where a function hands a result back to you. Now think about the other side of a function. What comes in. Sometimes you want a parameter to have a sensible value ready to go. That way the caller does not always have to pass one. That is exactly what a default parameter does.
🤔 Why Default Parameters?
Say you write a function that greets someone. Most of the time you want a normal “Hello”. But once in a while you want “Hi” or “Welcome” instead. Without defaults, you would have to pass the greeting word every single time, even when it is just the usual “Hello”. That gets annoying fast.
So here is the pain. Every call has to pass every argument. Miss one and Python stops and complains with an error. A default parameter fixes this. It gives the parameter a value to fall back on when the caller leaves it out.
A few reasons defaults are so handy:
- They mean fewer required arguments. The common call stays short and easy.
- They give you a sensible fallback, so the function still works when the caller is happy with the usual value.
- They keep old code working. If you add a new parameter with a default, every call written before today still runs fine, because the new parameter just uses its default. That is what people mean by a backward-compatible function.
🧩 What Is a Default Parameter?
A default parameter is a parameter that already has a value attached to it in the function definition. If the caller passes an argument, that argument wins. If the caller passes nothing, the default value is used instead.
Think of ordering coffee. If you say “a coffee, please” the shop gives you their standard cup. If you say “a large coffee” you get the large. The standard cup is the default. You only speak up when you want something different.
Here is the syntax. You write the parameter name, an equals sign, then the value.
def greet(name, greeting="Hello"): print(f"{greeting}, {name}!")So name is a normal required parameter, no default. And greeting="Hello" is the default part. It means this: if nobody gives me a greeting, I’ll use "Hello".
👋 Calling It With and Without an Argument
Let’s call greet both ways and watch what happens. First we leave the greeting out. Then we pass our own.
def greet(name, greeting="Hello"): print(f"{greeting}, {name}!")
greet("Alex")greet("Riya", "Welcome")This prints:
Output
Hello, Alex!Welcome, Riya!See the difference?
greet("Alex")passes only the name, sogreetingfalls back to"Hello".greet("Riya", "Welcome")passes both, sogreetingbecomes"Welcome"and the default is ignored.
The function did not break when we left the greeting out. That is the whole point. The default kept things working. And notice name has no default, so you always have to pass it. That feels right, because there is no sensible “default name”, but “Hello” is a perfectly sensible default greeting.
📦 A More Real Example
Let’s do something closer to real life. Say you are calculating the final price of an item after tax. The price changes every time, so that stays required. But the tax rate is usually the same, say 10%. So give it a default.
def final_price(price, tax_rate=0.10): return price + (price * tax_rate)
print(final_price(100))print(final_price(100, 0.18))This prints:
Output
110.00000000000001118.0Walk through it:
final_price(100)passes only the price, sotax_ratestays the default0.10. You get 100 plus 10% tax, which is 110.final_price(100, 0.18)passes a second value, so the default is replaced by0.18. You get 100 plus 18% tax, which is 118.
You write the common case once, the usual 10% tax, and you only override it when a different rate comes up. That tiny number with all those decimals is just how computers store fractions. We will not worry about it here.
🎯 Defaults With Positional and Keyword Calls
When you call a function, you can pass arguments in two ways. By position, where Python matches them left to right. Or by name, using name=value, which we call a keyword argument. Defaults work cleanly with both.
Here is a connect function. It needs a host, but the port and the timeout have sensible defaults.
def connect(host, port=8080, timeout=30): print(f"Connecting to {host}:{port} (timeout {timeout}s)")
connect("example.com")connect("example.com", 9000)connect("example.com", timeout=60)This prints:
Output
Connecting to example.com:8080 (timeout 30s)Connecting to example.com:9000 (timeout 30s)Connecting to example.com:9000 (timeout 60s)Look at what each call did:
- The first call passes only
host. So bothportandtimeoutuse their defaults. - The second call passes
9000by position, so it lands inport. Thetimeoutstill uses its default. - The third call skips
portentirely and setstimeout=60by name. Soportkeeps its default, and onlytimeoutchanges.
That third call is the real win. With a keyword argument you can jump straight to the one parameter you care about and leave everything before it on its default. You do not have to repeat the middle values just to reach the last one.
📐 The Ordering Rule
There is one rule you must follow. Parameters that have a default must come after parameters that do not.
Here is why this matters. When you call a function, Python fills the parameters from left to right. If a required parameter sat after a default one, Python would not know how to line up the values. So it refuses, before the function ever runs.
This is the right order. Required first, then default.
# ✅ Good: required parameter first, default afterdef book_seat(name, row="A"): print(f"{name} is seated in row {row}")
book_seat("Riya")This prints:
Output
Riya is seated in row AAnd here is the order that breaks.
# ❌ Avoid: default before a required parameterdef book_seat(row="A", name): print(f"{name} is seated in row {row}")Python rejects this right away. You get an error like this.
Output
SyntaxError: parameter without a default follows parameter with a defaultThe fix is to flip them, so the required name comes first and the default row comes after.
# ✅ Fixed: required first, default seconddef book_seat(name, row="A"): print(f"{name} is seated in row {row}")So the habit is simple. Put the must-have parameters on the left. Put the optional ones with defaults on the right.
⚠️ The Big Gotcha: Mutable Defaults
Now here is the one that surprises almost everyone. A mutable default is a default value that can be changed in place, like a list or a dictionary. And using one as a default does something you really do not expect.
Watch this. You want a function that adds an item to a list, and if no list is given, it should start a fresh empty one.
# ❌ Avoid: a list as a default valuedef add_item(item, items=[]): items.append(item) return items
print(add_item("apple"))print(add_item("milk"))print(add_item("bread"))You would expect three separate lists, right? But this is what actually prints:
Output
['apple']['apple', 'milk']['apple', 'milk', 'bread']The list keeps growing across calls. So what is going on?
- Python creates that default list one single time, when the function is defined, not each time you call it.
- So every call that uses the default shares the same one list.
- Each
appendadds to that one shared list. So items pile up call after call.
That is the trap. The default does not reset between calls. It is the same object every time.
The safe pattern is to use None as the default. Then build a fresh list inside the function whenever the default is in play.
# ✅ Good: use None, then make a new list each calldef add_item(item, items=None): if items is None: items = [] items.append(item) return items
print(add_item("apple"))print(add_item("milk"))print(add_item("bread"))This prints:
Output
['apple']['milk']['bread']Now each call gets its own clean list, so nothing leaks between calls. The trick is the if items is None: items = [] line. When the caller passes nothing, items is None, so we make a brand-new list right there inside the function. A new one every call.
For plain values like numbers, strings, and True/False, you never hit this issue, because you cannot change them in place. It only shows up with things you can change, like lists, dictionaries, and sets.
Tip
A quick rule of thumb. If your default would be a list, a dictionary, or a set, use None instead and create the real value inside the function.
⚠️ Common Mistakes
A few things trip people up at first.
- Putting a default parameter before a required one. Required parameters always come first, or you get a
SyntaxError. - Using a list or dictionary directly as a default. Use
Noneand build it inside instead. - Thinking the default runs fresh every call. It is created once at definition time, which is exactly why the mutable trap happens.
- Forgetting that a passed argument always overrides the default. If you pass a value, the default is ignored.
- Reaching for a positional argument when a keyword argument would be cleaner. If you only want to change the last parameter, name it, like
timeout=60, and skip the rest.
✅ Best Practices
- Give a default only when there is a genuinely sensible fallback value.
- Keep required parameters on the left, optional ones with defaults on the right.
- Use simple, unchangeable values as defaults: numbers, strings,
True,False, orNone. - When the natural default is a list or dictionary, use
Noneand create the real value inside the function. - Use keyword arguments at the call site to set just one optional value without repeating the others.
- Pick default values that match the most common way the function is called, so the short call just works.
🧩 What You’ve Learned
✅ A default parameter gives a parameter a value to use when the caller passes nothing.
✅ You write it as greeting="Hello" in the function definition, and a passed argument always overrides it.
✅ Defaults work with both positional and keyword arguments, so you can skip straight to the one value you want to change.
✅ Parameters with defaults must come after parameters without them, or you get a SyntaxError.
✅ Avoid mutable defaults like lists or dictionaries; they are created once and shared across calls, so use None and build the value inside the function.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What does a default parameter do?
Why: A default parameter supplies a fallback value that is used only when the caller does not pass an argument for it.
- 2
Given def greet(name, greeting="Hello"), what does greet("Alex") print?
Why: With no greeting passed, greeting falls back to its default "Hello", so it prints Hello, Alex!.
- 3
Why does def f(row="A", name) cause an error?
Why: Parameters with defaults must come after parameters without defaults, so a required parameter cannot follow a default one.
- 4
Why is using items=[] as a default risky, and what is the safe fix?
Why: A list default is created once at definition time and shared by every call, so it grows. Use None as the default and create a fresh list inside the function.
🚀 What’s Next?
Defaults let you skip arguments. But what if you want to pass them by name and in any order? That is where keyword arguments come in.