Functions, parameters & defaults
Write functions with default parameters and learn to read a traceback.
Functions, defaults, and reading errors
A function is how you stop copy-pasting the same logic into ten places. Name a block of code once, and every caller reuses it. Defaults take this further: they let one function serve many call sites without forcing every caller to spell out every argument. Picking a good default is real API design. When you call int("10") and get 10, that is int(x, base=10) quietly defaulting base to 10; give int("ff", 16) instead and you get 255. Most functions you use daily lean on defaults you never think about.
The mental model
def binds a name to a parameter list plus a body. The words in the parentheses are parameters (the names inside the function); the values you pass are arguments. Positional arguments fill parameters left to right. A default gives a parameter a fallback value that is used only when the caller omits that argument.
def power(base, exp=2):
return base ** exp
print(power(5)) # 25 exp falls back to 2, so 5 ** 2
print(power(2, 3)) # 8 exp is given as 3, so 2 ** 3
power(5) binds base to 5 and lets exp default to 2. power(2, 3) binds base to 2 and exp to 3. You can also pass by name in any order: power(exp=3, base=2) also returns 8.
| The call | base becomes | exp becomes | Result |
|---|---|---|---|
| power(5) | 5 | 2, the default | 25 |
| power(2, 3) | 2 | 3 | 8 |
| power(2, exp=3) | 2 | 3 | 8 |
| power(exp=3, base=2) | 2 | 3 | 8, since names free you from order |
| power() | nothing to bind | 2, the default | TypeError: missing a required argument |
| power(exp=3) | nothing to bind | 3 | TypeError, because a default cannot fill base |
A function can return any value, not just numbers. Your Practice builds and returns a string, so keep in mind that the result of a function is whatever object you hand to return.
Reading a traceback
When code raises an error, Python prints a traceback. Read it bottom-up.
Traceback (most recent call last):
File "main.py", line 4, in <module>
print(power("2", 3))
File "main.py", line 2, in power
return base ** exp
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
The last line names the error type and message: a TypeError because "2" is a str, and you cannot raise a string to a power. The frames above it are the call chain, newest at the bottom. Here they say the failure happened at return base ** exp, called from print(power("2", 3)). Read the last line first, then walk up only as far as you need.
Pitfall: default parameters must come last
Every parameter with a default has to appear after every parameter without one:
def power(exp=2, base): # SyntaxError: non-default argument follows default argument
return base ** exp
Python cannot fill positional arguments left to right if a required parameter sits behind an optional one. Fix it by ordering required parameters first: def power(base, exp=2).
Interview nuance: a default value is evaluated once, when def runs, not on each call, so a mutable default like bucket=[] is shared across calls and quietly accumulates results between them. The next lesson, References, copies and the mutable-default trap, covers why this happens and the None-sentinel fix in full.
def power(base, exp=2):
return base ** exp
print(power(5)) # 25 (exp defaults to 2)
print(power(2, 3)) # 8Apply
Your turn
The task this lesson builds to.
Implement power(base, exp=2): return base raised to the exp power, where exp
defaults to 2.
So power(3) is 9 (3 squared) and power(2, 3) is 8.
3 hints and 4 automated checks are waiting in the workspace.
Practice
Make it stick
A second problem on the same idea, so it survives past today.
Implement make_tag(name, content): wrap content in an HTML tag named name.
For ("b", "hi") return "<b>hi</b>".
2 hints and 3 automated checks are waiting in the workspace.