A function with two parameters where the second is usually the same value:
def add(a, b):
return a + b
add(3, 5) # caller always passes b
add(3, 5)
add(7, 5)If b is almost always 5, can I make that the default?
Exactly the use case for default arguments. Put =value after the parameter name in the def:
def add(a, b=5):
return a + b
add(3) # 8 — b defaults to 5
add(3, 7) # 10 — caller overridesAre there rules about which parameters can have defaults?
One rule: defaults come after required parameters. def f(a=1, b) is illegal — Python wouldn't know whether f(5) is calling with a=5 or with b=5. Required first, then optional.
And the default value — when is it evaluated?
Once, at def time. This is famous because it bites people:
def append_to(item, target=[]): # ⚠ shared mutable default
target.append(item)
return target
append_to(1) # [1]
append_to(2) # [1, 2] — same list reused!For today: stick to immutable defaults — numbers, strings, None, tuples. We'll come back to this trap in lesson 11.
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
greet("Ada") # 'Hello, Ada!'
greet("Ada", "Hi") # 'Hi, Ada!'The default applies when the caller omits the argument. Passing None is not omission — greet("Ada", None) passes None and you get "None, Ada!".
def ok(a, b=1, c=2): ... # legal
def bad(a=1, b, c=2): ... # SyntaxError — required after defaultIf you find yourself wanting required-after-default, you probably want keyword-only arguments — coming up in lesson 10.
The default expression runs at def time, not at each call:
import time
def f(t=time.time()):
return t
f() # always returns the time the def line ran, not the call timeThis trips people up most often with mutable defaults:
def append_to(item, target=[]): # ⚠
target.append(item)
return targetThe single [] created at def time is shared across every call that omits target. The fix — use None as the sentinel and create a fresh list inside:
def append_to(item, target=None):
if target is None:
target = []
target.append(item)
return targetif x: to mean "x not given"def f(x=None):
if not x: # ⚠ also true for 0, '', [], {} ...
x = default()Use if x is None: — that distinguishes "caller passed nothing" from "caller passed a falsy value."
A function with two parameters where the second is usually the same value:
def add(a, b):
return a + b
add(3, 5) # caller always passes b
add(3, 5)
add(7, 5)If b is almost always 5, can I make that the default?
Exactly the use case for default arguments. Put =value after the parameter name in the def:
def add(a, b=5):
return a + b
add(3) # 8 — b defaults to 5
add(3, 7) # 10 — caller overridesAre there rules about which parameters can have defaults?
One rule: defaults come after required parameters. def f(a=1, b) is illegal — Python wouldn't know whether f(5) is calling with a=5 or with b=5. Required first, then optional.
And the default value — when is it evaluated?
Once, at def time. This is famous because it bites people:
def append_to(item, target=[]): # ⚠ shared mutable default
target.append(item)
return target
append_to(1) # [1]
append_to(2) # [1, 2] — same list reused!For today: stick to immutable defaults — numbers, strings, None, tuples. We'll come back to this trap in lesson 11.
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
greet("Ada") # 'Hello, Ada!'
greet("Ada", "Hi") # 'Hi, Ada!'The default applies when the caller omits the argument. Passing None is not omission — greet("Ada", None) passes None and you get "None, Ada!".
def ok(a, b=1, c=2): ... # legal
def bad(a=1, b, c=2): ... # SyntaxError — required after defaultIf you find yourself wanting required-after-default, you probably want keyword-only arguments — coming up in lesson 10.
The default expression runs at def time, not at each call:
import time
def f(t=time.time()):
return t
f() # always returns the time the def line ran, not the call timeThis trips people up most often with mutable defaults:
def append_to(item, target=[]): # ⚠
target.append(item)
return targetThe single [] created at def time is shared across every call that omits target. The fix — use None as the sentinel and create a fresh list inside:
def append_to(item, target=None):
if target is None:
target = []
target.append(item)
return targetif x: to mean "x not given"def f(x=None):
if not x: # ⚠ also true for 0, '', [], {} ...
x = default()Use if x is None: — that distinguishes "caller passed nothing" from "caller passed a falsy value."
Create a free account to get started. Paid plans unlock all tracks.