Q&A
How to declare a function in Python
Overview
Functions are the primary building block for reusable code in Python. def defines them; Python is flexible with how you pass arguments, making function signatures expressive. Because functions are objects, they're also the foundation for decorators, higher-order programming, and callbacks.
graph TD
A["def name(params):"] --> B[Positional params]
A --> C["Default params: x=10"]
A --> D["*args: variadic positional"]
A --> E["**kwargs: variadic keyword"]
A --> F[return value or None]
style A fill:#1e1b4b,color:#fff
Basic Function
def greet(name):
"""Return a greeting string.""" # docstring (optional but good practice)
return f"Hello, {name}!"
print(greet("Ada")) # Hello, Ada!
A function without return returns None implicitly.
Default Parameter Values
def power(base, exp=2):
return base ** exp
power(3) # 9 (exp defaults to 2)
power(3, 3) # 27
Default values are evaluated once at definition time - a famous gotcha with mutable defaults:
# BAD: list is shared across all calls
def bad_append(x, lst=[]):
lst.append(x)
return lst
# GOOD: use None as sentinel
def good_append(x, lst=None):
if lst is None:
lst = []
lst.append(x)
return lst
Positional vs Keyword Arguments
At call time, arguments can be positional (by order) or keyword (by name):
def move(x, y, speed=1):
...
move(10, 20) # positional
move(x=10, y=20) # keyword
move(10, y=20, speed=3) # mixed (positional must come first)
*args - Variable Positional Arguments
def add(*numbers):
return sum(numbers)
add(1, 2, 3, 4) # 10
*args collects extra positional arguments into a tuple.
**kwargs - Variable Keyword Arguments
def describe(**attrs):
for k, v in attrs.items():
print(f"{k}: {v}")
describe(name="Ada", role="engineer")
**kwargs collects extra keyword arguments into a dict.
Keyword-Only Parameters (after *)
def connect(host, *, port=80, timeout=30):
...
connect("example.com") # port/timeout use defaults
connect("example.com", port=443) # keyword only - can't pass port positionally
Parameters after a bare * can only be passed by name.
Positional-Only Parameters (before /, Python 3.8+)
def circle_area(r, /):
return 3.14159 * r * r
circle_area(5) # works
circle_area(r=5) # ❌ TypeError - positional-only
Return Values
A function can return any object, or nothing (None), or multiple values via a tuple:
def minmax(lst):
return min(lst), max(lst) # returns a tuple
lo, hi = minmax([3, 1, 4, 1, 5]) # tuple unpacking
Lambda - Anonymous One-Expression Function
double = lambda x: x * 2
sorted(items, key=lambda item: item["price"])
lambda is limited to a single expression; use def for anything more complex.
Functions Are First-Class
def apply(func, value):
return func(value)
apply(str.upper, "hello") # "HELLO"
apply(lambda x: x * 3, 4) # 12
Nested Functions and Closures
def outer(x):
def inner(y):
return x + y # inner captures x from the enclosing scope
return inner
add5 = outer(5)
add5(3) # 8
Why This Matters in Interviews
Python interviews use function declaration to test argument mechanics. Key points: default-mutable-argument pitfall (use None sentinel), *args/**kwargs for variadic signatures, keyword-only parameters (after *), functions as first-class objects, and closures. Understanding call-time positional vs keyword matching and the parameter ordering rules (positional / standard * keyword-only **kwargs) are common follow-ups.
Common Pitfalls
- Mutable default argument (
def f(lst=[])) - shared across calls; useNoneas the default. - Positional argument after a keyword argument at the call site -
SyntaxError. - Parameters-after-
*being positional accidentally (they're keyword-only). - Returning a
tuplethinking it returns multiple "values" (it does, but as one object). - Using
lambdawhen a nameddefwould be clearer and testable.
References
- Python functions tutorial: https://docs.python.org/3/tutorial/controlflow.html#defining-functions
- Python function call semantics: https://docs.python.org/3/reference/expressions.html#calls