Skip to content

Functions

A function is a reusable, named block of code defined with def. It can accept parameters (with optional default values), and returns a value with return (or None implicitly if there’s no return). Functions are the primary tool for avoiding repeated code and giving logic a clear name.

def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
print(greet("Alice")) # Hello, Alice!
print(greet("Bob", greeting="Hi")) # Hi, Bob!
def add(*numbers): # accepts any number of positional args
return sum(numbers)
print(add(1, 2, 3, 4)) # 10
def describe(**details): # accepts any number of keyword args
for key, value in details.items():
print(f"{key}: {value}")
describe(name="Alice", age=30)

Using a mutable default argument (like a list or dict) β€” the default is created once when the function is defined, not each time it’s called, so it silently persists and accumulates state across calls.

def add_item(item, items=[]): # BUG: same list reused every call
items.append(item)
return items
print(add_item("a")) # ['a']
print(add_item("b")) # ['a', 'b'] <- unexpected! carried over from last call
# Fixed:
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
  1. What does a function return if it has no explicit return statement?

    AnswerNone.
  2. What’s the danger of def f(items=[]):?

    AnswerThe default list is created once at function-definition time and shared across every call that doesn't pass its own items, so mutations accumulate unexpectedly between calls.
  3. What’s the difference between *args and **kwargs?

    Answer*args collects extra positional arguments into a tuple; **kwargs collects extra keyword arguments into a dict.