Functions & Modules
Functions
Define reusable functions with def: parameters and return values, default and keyword arguments, *args and **kwargs, scope and the LEGB rule, and docstrings.
def greet(name):
"""Return a friendly greeting.""" # docstring: documents the function
return f"Hello, {name}!"
def area_and_perimeter(length, width):
return length * width, 2 * (length + width) # return several values (a tuple)
print(greet("Asha"))
a, p = area_and_perimeter(7, 3)
print(a, p)
print(greet.__doc__)Hello, Asha! 21 20 Return a friendly greeting.
A function without a return statement returns None.
Arguments: positional, keyword and default
def simple_interest(principal, rate=7.5, years=1):
return principal * rate * years / 100
print(simple_interest(10000)) # defaults used
print(simple_interest(10000, 8)) # positional
print(simple_interest(10000, years=3)) # keyword: skip rate
print(simple_interest(years=2, principal=5000)) # any order with keywords750.0 800.0 2250.0 750.0
*args and **kwargs
*args collects any number of extra positional arguments into a tuple; **kwargs collects extra keyword arguments into a dict.
def average(*numbers):
return sum(numbers) / len(numbers) if numbers else 0
def make_profile(name, **details):
lines = [f"Name: {name}"] + [f"{k.title()}: {v}" for k, v in details.items()]
return "\n".join(lines)
print(average(4, 8, 15, 16, 23, 42))
print(make_profile("Ravi", course="CSE", year=2, city="Chennai"))
values = [3, 9, 27]
print(average(*values)) # * unpacks a list into arguments18.0 Name: Ravi Course: CSE Year: 2 City: Chennai 13.0
Scope: the LEGB rule
When Python looks up a name, it searches four scopes in order: Local → Enclosing (outer function) → Global (module) → Built-in.
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print("inner sees:", x)
inner()
print("outer sees:", x)
outer()
print("module sees:", x)
counter = 0
def increment():
global counter # needed to ASSIGN to a global
counter += 1
increment(); increment()
print("counter =", counter)inner sees: local outer sees: enclosing module sees: global counter = 2
nonlocal does the same for a variable in an enclosing function. Prefer returning values over modifying globals.
How arguments are passed
Python passes references to objects ("call by object reference"). If a function mutates a mutable argument, the caller sees the change; rebinding the parameter does not affect the caller.
def mutate(lst):
lst.append(99) # changes the caller's list
def rebind(lst):
lst = [0] # only changes the local name
nums = [1, 2]
mutate(nums)
rebind(nums)
print(nums)[1, 2, 99]
Functions are objects
Functions can be stored in variables, passed to other functions and returned from them:
def apply_twice(f, x):
return f(f(x))
def make_multiplier(n):
def multiply(x):
return x * n # remembers n: a closure
return multiply
double = make_multiplier(2)
triple = make_multiplier(3)
print(apply_twice(double, 5), triple(7))
operations = {"+": lambda a, b: a + b, "*": lambda a, b: a * b}
print(operations["*"](6, 7))20 21 42
Type hints
Optional annotations document expected types (Python does not enforce them, but editors and checkers do):
def bmi(weight_kg: float, height_m: float) -> float:
return round(weight_kg / height_m ** 2, 1)
print(bmi(72.5, 1.68))25.7
Test yourself
4 questions · pick an answer to check it instantly
- 1
What does a function return if it has no
returnstatement?Every function returns something; withoutreturnit isNone. - 2
What is
average(*[2, 4, 6])ifdef average(*nums): return sum(nums) / len(nums)?*unpacks the list into three arguments, which*numscollects again. - 3
What does
global xinside a function allow?Reading a global works anyway; assigning needs theglobaldeclaration. - 4
Why is
def f(items=[])dangerous?Defaults are evaluated once; useNoneand create the list inside.