Start with the study notes on Functions (definitions, examples and a quick revision), then work through 30 practice questions, each with the correct answer and a clear explanation of why it is right. When you are ready, take this topic as an interactive quiz to test yourself.
8 easy Β· 14 medium Β· 8 hard Β· Part of Python: Master Python from basics to advanced - 20 topics.
Topic overview
Functions β reusable blocks of code
1. Defining and calling a function
A function is a named block of code that runs when you call it. You define it with def; its body runs only when it is called.
Variables created inside a function are local: they exist only while the function runs. Functions can read global variables, but assigning to one needs the global keyword. Usually it is better to pass values in and return them.
x = 5
def show():
print(x) # reading a global is fine
show()
count = 0
def inc():
global count
count += 1
inc()
print(count)
Output5
1
Remember: Assigning to a name anywhere in a function makes it local for the whole function. Reading it before the assignment gives UnboundLocalError.
7. The mutable default trap
A default value is created once, when the function is defined. A default list is therefore shared between calls. Use None and create the list inside.
A recursive function calls itself on a smaller problem, and must have a base case that stops it. Without one, Python raises RecursionError (the default limit is about 1,000 calls).
def factorial(n):
if n <= 1: # base case
return 1
return n * factorial(n - 1)
print(factorial(5))
Output120
10. Functions are objects
Functions can be stored in variables, kept in lists, passed to other functions and returned. This is called being first-class.
def shout(text):
return text.upper()
def apply(func, value):
return func(value)
f = shout # no brackets: the function itself
print(f("hi"))
print(apply(len, "hello"))
OutputHI
5
Example: Type hints such as def add(a: int, b: int) -> int: document the expected types, but Python does not enforce them.
11. Common misconceptions
Misconception
Correct understanding
print and return do the same job.
print only shows a value; return gives it back for further use.
A default list starts fresh on every call.
It is created once and shared; use None as the default.
Variables made inside a function exist outside it.
They are local and disappear when the function ends.
def f(a=1, b): is fine.
Parameters with defaults must come after required ones.
Type hints stop wrong types at runtime.
Python ignores them at runtime; tools like mypy check them.
12. Quick revision
def
Defines a function.
Parameter / argument
The placeholder / the actual value passed.
return
Sends a value back and exits the function.
Default argument
Used when the caller leaves it out.
*args
Extra positional arguments, as a tuple.
**kwargs
Extra keyword arguments, as a dict.
lambda
A small one-expression function.
Recursion
A function calling itself, with a base case.
13. Test your understanding
What does a function without return give back?
Show answer
None
In def greet(name), what is name called?
Show answer
A parameter.
What does *args collect?
Show answer
Extra positional arguments, as a tuple.
What does **kwargs collect?
Show answer
Extra keyword arguments, as a dict.
What is wrong with def add(item, lst=[]):?
Show answer
The default list is shared between calls.
How do you sort words by length?
Show answer
sorted(words, key=len)
What must every recursive function have?
Show answer
A base case.
How do you return two values?
Show answer
return a, b (it returns a tuple).
Key takeaway: Functions package code behind a name: take inputs as parameters, give results back with return, and beware the shared mutable default.
Notes written by Velnov. Every code example with an output was run in Python 3 to check that the output shown is exactly what Python prints.
Which is the correct way to define a function called greet?
def greet():
print("Hello!")
greet() # call it - Hello!
A def greet():
B function greet():
C define greet():
D greet() =
Answer: A β def greet():
Functions are defined with the def keyword, the name, parentheses for parameters, and a colon. The body is indented below.
defFunction calls
Q2.easy
What happens if you define a function but never CALL it?
def greet():
print("Hi") # not printed yet!
greet() # NOW it prints
A Its body runs once automatically
B Nothing - the body only runs when the function is called
C SyntaxError
D It runs at the end of the program
Answer: B β Nothing - the body only runs when the function is called
def only CREATES the function. The body executes each time you call it with parentheses: greet(). Defining without calling produces no output.
defCalling
Q3.easy
What does a function return if it has no return statement?
def greet(name):
print(f"Hello {name}")
result = greet("Ana") # prints Hello Ana
print(result) # None
A 0
B Empty string
C None
D Error
Answer: C β None
Every function returns something. Reaching the end without return (or a bare return) gives None.
print displays; return hands a value back - the classic beginner confusion.
Nonereturn
Q4.easy
What is the difference between print() and return in a function?
def add(a, b):
return a + b
total = add(2, 3) * 10 # 50 - reusable!
A None - they do the same
B print shows a value on screen; return sends the value back to the caller for further use
C return shows on screen; print sends back
D return only works with numbers
Answer: B β print shows a value on screen; return sends the value back to the caller for further use
print() is for humans (display only); return is for code - the caller receives the value and can store, compare or reuse it. A function that only prints can't have its result used in calculations.
returnprint()
Q5.easy
In def greet(name): ... and the call greet("Ana") - what are name and "Ana" called?
A name is the parameter; "Ana" is the argument
B name is the argument; "Ana" is the parameter
C Both are parameters
D Both are arguments
Answer: A β name is the parameter; "Ana" is the argument
PARAMETER = the placeholder in the definition (name). ARGUMENT = the actual value passed when calling ("Ana"). Parameter is the parking spot, argument is the car.
ParametersArguments
Q18.easy
What does return do the moment it executes?
def check(n):
if n < 0:
return "negative" # exits here for negatives
return "positive"
print("never runs")
A Pauses the function
B Immediately EXITS the function, handing back the value - remaining lines don't run
C Prints the value
D Restarts the function
Answer: B β Immediately EXITS the function, handing back the value - remaining lines don't run
return ends the function on the spot. Code after an executed return is unreachable. Multiple returns are fine - typically one per branch.
returnEarly return
Q25.easy
By PEP 8 convention, function names should be written as:
A snake_case: calculate_total()
B CamelCase: CalculateTotal()
C ALLCAPS: CALCULATETOTAL()
D kebab-case: calculate-total()
Answer: A β snake_case: calculate_total()
Functions and variables use snake_case; CamelCase is reserved for class names; ALL_CAPS for constants. (kebab-case is illegal - hyphens mean subtraction.)
PEP 8Naming conventions
Q30.easy
Why split a program into functions at all?
A Reuse, readability, easier testing, and no copy-pasted code
B Functions make programs run faster automatically
C Python requires at least 5 functions
D Only to use return
Answer: A β Reuse, readability, easier testing, and no copy-pasted code
Functions package logic behind a name: write once, call anywhere, test in isolation, and read programs as a story of well-named steps. The DRY principle: Don't Repeat Yourself.
DRY principleCode organization
Medium questions (14)
Q6.medium
What is the output?
def greet(name="Guest"):
print("Hello", name)
greet()
A To accept any number of positional arguments as a tuple
B To multiply arguments
C To pass arguments by reference
D To accept keyword arguments as a dict
Answer: A β To accept any number of positional arguments as a tuple
*args collects extra positional arguments into a tuple, so the function accepts any count: total(1), total(1,2,3)... **kwargs does the same for keyword arguments (into a dict).
*args**kwargs
Q10.medium
What does **kwargs collect in def config(**kwargs)?
def config(**kwargs):
for key, value in kwargs.items():
print(key, "=", value)
config(theme="dark", size=12)
A Extra POSITIONAL arguments as a list
B Extra KEYWORD arguments as a dictionary
C Only two arguments
D Global variables
Answer: B β Extra KEYWORD arguments as a dictionary
**kwargs gathers surplus name=value arguments into a dict: config(theme="dark", size=12) gives kwargs = {'theme': 'dark', 'size': 12}.
**kwargsDictionaries
Q11.medium
What is the output?
def stats(nums):
return min(nums), max(nums)
lo, hi = stats([3, 8, 1])
print(lo, hi)
def stats(nums):
return min(nums), max(nums) # a tuple
lo, hi = stats([3, 8, 1])
print(lo, hi) # 1 8
A 1 8
B 3 8
C (1, 8)
D Error - can't return two values
Answer: A β 1 8
Returning two values actually returns ONE tuple (1, 8), which unpacks into lo and hi. This is how Python 'returns multiple values'.
TuplesUnpacking
Q12.medium
What is a lambda function?
double = lambda x: x * 2
print(double(5)) # 10
# same as:
def double(x):
return x * 2
A A small anonymous one-expression function
B A function that runs in parallel
C A Greek module
D A function with no arguments
Answer: A β A small anonymous one-expression function
lambda arguments: expression creates a tiny unnamed function in one line. Best used inline where a short function is needed once - e.g. as a sort key.
lambdaAnonymous functions
Q14.medium
What happens?
def f():
x = 10
f()
print(x)
def f():
x = 10
return x
x = f() # bring it out via return
print(x) # 10
A Prints 10
B NameError - x is local to f and doesn't exist outside
C Prints None
D Prints 0
Answer: B β NameError - x is local to f and doesn't exist outside
Variables created inside a function are LOCAL - they exist only while the function runs and vanish afterwards. To get the value out, return it.
Local scopeNameError
Q15.medium
What is the output?
x = 5
def show():
print(x)
show()
x = 5
def show():
print(x) # reading global - fine
show() # 5
A 5 - functions can READ global variables
B NameError
C None
D 0
Answer: A β 5 - functions can READ global variables
Functions can freely READ globals (Python looks outward: Local -> Enclosing -> Global -> Built-in, the LEGB rule). Only ASSIGNING to them requires the global keyword.
LEGB ruleglobal
Q19.medium
def f(a, b): ... What happens when you call f(1)?
def f(a, b):
return a + b
# f(1) # TypeError
f(1, 2) # 3
A b becomes None
B b becomes 0
C TypeError: f() missing 1 required positional argument: 'b'
Parameters without defaults are REQUIRED. Calling with too few (or too many) arguments raises TypeError with a helpful message naming the missing parameter.
TypeErrorRequired arguments
Q21.medium
What is recursion?
def factorial(n):
if n == 0: # base case
return 1
return n * factorial(n - 1)
print(factorial(5)) # 120
A A function that calls ITSELF, with a base case to stop
B A loop inside a function
C Calling two functions at once
D A function without a return
Answer: A β A function that calls ITSELF, with a base case to stop
A recursive function solves a problem by calling itself on a smaller piece, stopping at a base case. Classic example: factorial(n) = n * factorial(n-1), stopping at factorial(0) = 1.
RecursionBase case
Q23.medium
What is the output of factorial(3) using:
def factorial(n):
if n <= 1: return 1
return n * factorial(n - 1)
What are the type hints in: def add(a: int, b: int) -> int:
def add(a: int, b: int) -> int:
return a + b
add("x", "y") # runs! returns 'xy' - hints not enforced
A Optional annotations documenting expected types - not enforced at runtime
B Strict type checks that raise errors on wrong types
C Comments
D Required in Python 3
Answer: A β Optional annotations documenting expected types - not enforced at runtime
Hints document intent and let tools (IDEs, mypy) catch type mistakes - but Python itself ignores them at runtime: add("a", "b") still runs. -> int hints the return type.
Type hintsmypy
Q28.medium
How do you read a function's docstring?
def area(r):
"""Return the area of a circle with radius r."""
return 3.14159 * r * r
print(area.__doc__)
help(area)
A help(func) or func.__doc__
B func.doc()
C docstring(func)
D You can't after definition
Answer: A β help(func) or func.__doc__
The triple-quoted string right under def is stored in func.__doc__ and shown by help(func). Good docstrings say what the function does, its parameters and return value.
Docstringshelp()
Q29.medium
What is the output?
def outer():
def inner():
return "hi"
return inner()
print(outer())
Functions can be defined INSIDE other functions. outer defines inner, calls it (note the parentheses in 'return inner()'), and returns its result: 'hi'. Returning inner WITHOUT parentheses would return the function itself - that's how closures work (next topics!).
Nested functionsClosures
Hard questions (8)
Q8.hard
Which call is INVALID for def f(a, b)?
f(1, 2) # ok - all positional
f(1, b=2) # ok - positional then keyword
# f(a=1, 2) # SyntaxError!
A f(1, 2)
B f(a=1, b=2)
C f(1, b=2)
D f(a=1, 2)
Answer: D β f(a=1, 2)
Positional arguments must come BEFORE keyword arguments. f(a=1, 2) puts a positional after a keyword - SyntaxError: positional argument follows keyword argument.
Argument orderSyntaxError
Q13.hard
What does the key argument do here?
words = ["banana", "fig", "apple"]
print(sorted(words, key=len))
A ['fig', 'apple', 'banana'] - sorts by word LENGTH
B ['apple', 'banana', 'fig'] - alphabetical
C Removes short words
D Error
Answer: A β ['fig', 'apple', 'banana'] - sorts by word LENGTH
key tells sorted() what to compare - here each word's len(): 3, 5, 6 -> fig, apple, banana. Lambdas are common keys: sorted(students, key=lambda s: s["marks"]).
sorted()lambdakey functions
Q16.hard
Why does this raise UnboundLocalError?
count = 0
def inc():
count = count + 1
inc()
B The assignment makes count LOCAL for the whole function, so 'count + 1' reads a local that has no value yet
C Functions can't use numbers
D inc needs a parameter
Answer: B β The assignment makes count LOCAL for the whole function, so 'count + 1' reads a local that has no value yet
Because the function ASSIGNS to count, Python treats count as local everywhere in the function - including the right-hand side, where it hasn't been given a value yet. Fix: declare 'global count', or better, pass it in and return the new value.
UnboundLocalErrorglobalScope
Q17.hard
THE classic Python trap - what is the output?
def add(item, lst=[]):
lst.append(item)
return lst
print(add(1))
print(add(2))
def add(item, lst=None):
if lst is None:
lst = [] # fresh list per call
lst.append(item)
return lst
A [1] then [2]
B [1] then [1, 2] - the SAME default list is reused between calls
C [1, 2] then [1, 2]
D Error
Answer: B β [1] then [1, 2] - the SAME default list is reused between calls
Default values are created ONCE, at function definition - not per call. Every call without lst shares the same list, which keeps growing. Fix: default to None and create the list inside.
Asked in countless interviews - the 'mutable default argument' trap.
Parameters WITH defaults must come AFTER those without: def f(a=1, b): is a SyntaxError - Python couldn't tell which argument f(5) fills. Required first, optional after.
Default argumentsParameter order
Q22.hard
What happens to a recursive function with NO base case?
def broken(n):
return broken(n - 1) # no base case!
# broken(5) # RecursionError
A It runs forever silently
B RecursionError: maximum recursion depth exceeded
C It returns None
D SyntaxError at definition
Answer: B β RecursionError: maximum recursion depth exceeded
Each call stacks up; without a stopping condition Python hits its recursion limit (about 1000 calls) and raises RecursionError - its protection against a true infinite recursion crash.
RecursionErrorCall stack
Q26.hard
What does this show?
def shout(text): return text.upper()
f = shout
print(f("hi"))
f = shout # no () - the function itself
print(f("hi")) # HI
ops = [shout, str.lower] # functions in a list!
A HI - functions are objects that can be assigned to variables
B Error - functions can't be assigned
C hi
D shout
Answer: A β HI - functions are objects that can be assigned to variables
Functions are first-class objects: assign them, store them in lists, pass them to other functions. f = shout (NO parentheses) makes f another name for the same function; f("hi") calls it.
shout() with parentheses would CALL it; without, you refer to it.
First-class functionsCallbacks
Q27.hard
What is the output?
def apply(func, value):
return func(value)
print(apply(len, "hello"))
apply receives the len function itself and calls it on "hello" -> 5. Passing functions as arguments is the foundation of callbacks, map(), sorted(key=...), and decorators.