Defining and Using Functions in Python
def area_of_circle(radius): # def, name, parameters, colon
"""Return the area of a circle of the given radius."""
return 3.14159 * radius * radius
a = area_of_circle(5) # call
Four parts: the def keyword, a name, parameters in brackets, and an
indented body. The string on the first line is a docstring — optional
but good practice, and it is what help(area_of_circle) shows.
Parameters and arguments
- Parameter — the name in the definition (
radius) - Argument — the value passed at the call (
5)
Worth keeping straight because exam questions use the terms precisely.
return
return sends a value back and exits immediately:
def grade_for(marks):
if marks >= 90:
return "S" # exits here; nothing below runs
if marks >= 50:
return "P"
return "F"
No else is needed — reaching the second if already means the first
returned nothing.
A function with no return returns None:
def show(x):
print(x) # prints, returns nothing
result = show(5) # result is None
That distinction matters: print displays, return gives back. A
function that prints its answer cannot have that answer used in a
calculation. This is the single most common misunderstanding in this
topic.
def bad_square(n):
print(n * n) # cannot be used further
def good_square(n):
return n * n
total = good_square(3) + good_square(4) # 25
total = bad_square(3) + bad_square(4) # TypeError: None + None
Default and keyword arguments
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
greet("Anjali") # 'Hello, Anjali!'
greet("Rahul", "Good morning") # 'Good morning, Rahul!'
greet(greeting="Hi", name="Meera") # order does not matter when named
Parameters with defaults must come after those without.
One trap worth knowing: never use a mutable default.
def add_mark(mark, marks=[]): # WRONG
marks.append(mark)
return marks
add_mark(90) # [90]
add_mark(80) # [90, 80] — the SAME list, still there
The default is created once when the function is defined, not on each
call. Use None instead:
def add_mark(mark, marks=None):
if marks is None:
marks = []
marks.append(mark)
return marks
Scope
Names created inside a function are local — they exist only during the call:
def f():
x = 10 # local
return x
f()
print(x) # NameError — x does not exist out here
A function can read a global name but not rebind it without saying so:
count = 0
def increment():
count = count + 1 # UnboundLocalError
def increment_properly():
global count # explicit
count += 1
global is usually a sign the design is wrong. Prefer passing values in
and returning results out — a function whose behaviour depends only on
its arguments is far easier to test and reason about.
Mutable arguments
Passing a list gives the function access to the same list, so changes are visible to the caller — the aliasing point from the lists topic:
def append_zero(items):
items.append(0) # modifies the caller's list
data = [1, 2]
append_zero(data)
print(data) # [1, 2, 0]
If you do not want that, pass a copy: append_zero(data.copy()).