Functions
Functions are the primary unit of reuse in Python. Write it once, call it everywhere. Learn how to define them, pass data in, get data out, and use Python's powerful parameter options.
Defining and Calling Functions
A function is a named block of code. You define it once with def, then call it as many times as you need.
# Basic function
def greet():
print("Hello, World!")
greet() # Hello, World!
# With parameters and return value
def add(a, b):
return a + b
result = add(3, 5)
print(result) # 8
print(add(10, 20)) # 30 — can call inline too
# With type hints (recommended — self-documents the code)
def multiply(x: int, y: int) -> int:
"""Multiply two integers and return the result."""
return x * y
print(multiply(4, 7)) # 28
Output
Tip — return vs printPrefer
return over print inside functions. A returned value can be stored, passed to other functions, and tested. A printed value is gone.Parameters: Default, *args, **kwargs
Python gives you flexible ways to pass data into functions.
# Default parameters
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
print(greet("Alice")) # Hello, Alice!
print(greet("Bob", "Hey")) # Hey, Bob!
# *args — any number of positional arguments (tuple inside)
def total(*numbers):
return sum(numbers)
print(total(1, 2, 3)) # 6
print(total(1, 2, 3, 4, 5)) # 15
# **kwargs — any number of keyword arguments (dict inside)
def create_user(**info):
for key, val in info.items():
print(f" {key}: {val}")
create_user(name="Alice", age=25, city="Paris")
# Combining all three
def demo(required, *args, default="X", **kwargs):
print(f"required={required}, args={args}")
print(f"default={default}, kwargs={kwargs}")
demo("must", 1, 2, default="Y", extra="hi")
Output
Lambda Functions
A lambda is a tiny anonymous function that fits in one expression. Perfect for quick operations you'd pass as an argument.
double = lambda x: x * 2
print(double(5)) # 10
# Real use case: sorting with a key
students = [("Alice", 85), ("Bob", 92), ("Carol", 78)]
students.sort(key=lambda s: s[1]) # sort by grade
print(students) # [('Carol', 78), ('Alice', 85), ('Bob', 92)]
# map() and filter() with lambdas
nums = [1, 2, 3, 4, 5, 6]
squared = list(map(lambda x: x**2, nums))
evens = list(filter(lambda x: x % 2 == 0, nums))
print(squared) # [1, 4, 9, 16, 25, 36]
print(evens) # [2, 4, 6]
# Modern alternative — usually prefer list comprehension
squared2 = [x**2 for x in nums] # same result, more readable
Output
Scope: Local vs Global
message = "I'm global" # global scope
def show():
local_msg = "I'm local"
print(message) # can READ globals
print(local_msg)
show()
print(message)
# print(local_msg) # NameError — local_msg doesn't exist here
# Modify a global with 'global'
counter = 0
def increment():
global counter
counter += 1
increment(); increment()
print(counter) # 2
# Closures — inner function captures outer variable
def make_adder(n):
def add(x):
return x + n # n is "closed over"
return add
add5 = make_adder(5)
print(add5(10)) # 15
print(add5(20)) # 25
Output
Avoid globalUsing
global makes code hard to reason about. Prefer passing values as arguments and returning results instead.Recursion
A function that calls itself. Every recursive function needs a base case (the stopping condition) and a recursive case (the step toward the base).
def factorial(n: int) -> int:
if n <= 1: # base case
return 1
return n * factorial(n - 1) # recursive case
print(factorial(5)) # 120 (5 * 4 * 3 * 2 * 1)
print(factorial(10)) # 3628800
# Fibonacci
def fib(n):
if n <= 1: return n
return fib(n-1) + fib(n-2)
print([fib(i) for i in range(10)])
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Output