def
def builds a function object and binds it to a name. Defaults are evaluated once, at definition time — the source of Python’s most famous trap.
def
def name(a, b): body return value
defaults
def name(a, b=10): ...
*args / **kwargs
def name(*args, **kwargs): ...
/ and * markers
def name(a, /, b, *, c): ...
Use for
Any reusable piece of logic that takes inputs and returns a result
Result
a statement — binds a function object to the name
Pairs with
return, lambda, yield, @decorators, *args, **kwargs
Watch out
default values are created once, not per call — never use [] or {} as a default
Demo
Live evaluation
low and high have defaults. Pass high by keyword and leave low alone.
Try:
Inputs
xintvalue to clamp
highintupper bound
Code
def clamp(x, low=0, high=10): return max(low, min(x, high)) (clamp(7), clamp(7, high=5))
Result
(7, 5)
In the mutable default tab, a and b are the same list object (a is b is True) — the second call appended to the list the first call returned. In the *args tab, an empty list leaves nothing for the required first parameter, so the call fails before the body runs.
Syntax slots
| Name | Type | Required | Description |
|---|---|---|---|
| name | name | yes | The variable the new function object is bound to. Rebinding it later replaces the function. |
| parameters | parameter list | no | Plain names, name=default, *args (extra positionals as a tuple), **kwargs (extra keywords as a dict). / ends positional-only, a bare * starts keyword-only. |
| default | expression | no | Evaluated ONCE when def runs, and the same object is reused by every call that omits the argument. |
| body | block | yes | Runs on each call. Ends at return (or falls off the end, which returns None). |
Common patterns
None as the "no value" default
The safe replacement for a mutable default.
def add(item, bucket=None): if bucket is None: bucket = [] bucket.append(item) return bucket
Keyword-only options
Everything after * must be passed by name, so call sites stay readable.
def connect(host, port, *, timeout=5, retries=3): ...
Forwarding arguments
A wrapper that passes everything through unchanged.
def logged(func): def wrapper(*args, **kwargs): print("calling", func.__name__) return func(*args, **kwargs) return wrapper
Docstring first
A string literal as the first statement becomes func.__doc__ (shown by help()).
def area(w, h): """Return the area of a w x h rectangle.""" return w * h
Examples
1. Define and call
def greet(name):
return f'Hello, {name}!'
greet('Ann')
Returns
'Hello, Ann!'2. Default and keyword arguments
def power(base, exp=2):
return base ** exp
(power(3), power(2, exp=10))
Returns
(9, 1024)3. *args and **kwargs
def f(*args, **kwargs):
return args, kwargs
f(1, 2, x=3)
Returns
((1, 2), {'x': 3})4. Positional-only and keyword-only
def f(a, /, b, *, c):
return a, b, c
f(1, b=2, c=3)
Returns
(1, 2, 3)5. Breaking the markers
def f(a, /, b, *, c):
return a, b, c
f(1, 2, 3)
Returns
TypeError: f() takes 2 positional arguments but 3 were given6. Functions are objects
def square(x):
return x * x
ops = {'sq': square}
(ops['sq'](4), square.__name__)
Returns
(16, 'square')7. A decorator with @
def shout(func):
def wrapper(*args):
return func(*args).upper()
return wrapper
@shout
def greet(name):
return f'hi {name}'
greet('bo')
Returns
'HI BO'Pitfalls
1. A mutable default argument
The default list is created once when def runs and shared by every call that omits the argument.
bucket=[]
def add(item, bucket=[]): bucket.append(item) return bucket add(1) add(2)
[1, 2]
bucket=None
def add(item, bucket=None): if bucket is None: bucket = [] bucket.append(item) return bucket add(1) add(2)
[2]
2. Forgetting return
A function that computes a value but never returns it gives back None.
no return
def double(x): x * 2 print(double(4))
None
return it
def double(x): return x * 2 print(double(4))
8
3. Referencing the function instead of calling it
Without parentheses you get the function object, which is never equal to its result.
no ()
def get(): return 42 result = get result == 42
False
call it
def get(): return 42 result = get() result == 42
True
4. A required parameter after a default
Once a parameter has a default, every positional parameter after it needs one too.
b after a=1
compile('def f(a=1, b): pass', '<demo>', 'exec')
SyntaxError: parameter without a default follows parameter with a default
required first
def f(b, a=1): return a, b f(2)
(1, 2)
When to use
Use it
- Logic you call from more than one place
- Anything longer than one expression, or that needs statements, a docstring or a name in tracebacks
- Giving a meaningful name to a step of a larger computation
Reach for something else
- A tiny one-off key= or callback expression → lambda
- A function that produces a series of values → still def, but with yield (a generator)
- Several functions sharing state → a class may read better than closures
Notes
CPython impl
def is executed at runtime: it evaluates the defaults, builds a function object from pre-compiled code and binds it to the name
Defaults
Stored in func.__defaults__ (and __kwdefaults__ for keyword-only) — a mutated default is visible there
Scope
Names assigned inside the body are local unless declared global or nonlocal
FAQ
*args collects any extra positional arguments into a tuple, **kwargs collects any extra keyword arguments into a dict. The names are convention; the * and ** are what matter. At a call site, * and ** do the reverse: f(*items, **options) unpacks them into arguments.
History
3.8
The / function parameter syntax may be used to indicate positional-only parameters (PEP 570).
3.9
Functions may be decorated with any valid assignment expression.
3.12
Type parameter lists (def f[T](x: T)) are new.