Functions are the building blocks of reusable code. Mastering functions, decorators, and functional programming patterns will make you a significantly better Python developer.
def greet(name):
"""Greet a person by name."""
return f"Hello, {name}!"
Call the function
message = greet("Alice")
print(message) # Hello, Alice!Anatomy:
def - keyword to define functiongreet - function name (use lowercase with underscores)(name) - parameter(s)"""docstring""" - optional documentationreturn - sends value back to callerdef add(a, b):
return a + b
def describe_person(name, age, city):
return f"{name} is {age} years old and lives in {city}."
result = add(5, 3) # 8
info = describe_person("Alice", 30, "NYC")def power(base, exponent=2):
return base ** exponent
print(power(5)) # 25 (uses default exponent=2)
print(power(5, 3)) # 125 (overrides default)
def create_user(username, role="user", active=True):
return {
"username": username,
"role": role,
"active": active
}
user1 = create_user("alice")
# {'username': 'alice', 'role': 'user', 'active': True}
user2 = create_user("admin", role="admin")
# {'username': 'admin', 'role': 'admin', 'active': True}def configure(host, port, timeout, retry):
print(f"{host}:{port}, timeout={timeout}, retry={retry}")
Positional (must match order)
configure("localhost", 8080, 30, True)
Named (any order)
configure(port=8080, host="localhost", retry=True, timeout=30)
Mix (positional first, then keyword)
configure("localhost", 8080, timeout=30, retry=True)def sum_all(*numbers):
"""Sum any number of arguments."""
total = 0
for num in numbers:
total += num
return total
print(sum_all(1, 2, 3)) # 6
print(sum_all(10, 20, 30, 40)) # 100
Example: flexible printing
def print_items(*items, sep=" | "):
print(sep.join(str(item) for item in items))
print_items("A", "B", "C") # A | B | C
print_items(1, 2, 3, sep=", ") # 1, 2, 3def create_profile(**details):
"""Create profile from any keyword arguments."""
for key, value in details.items():
print(f"{key}: {value}")
create_profile(name="Alice", age=30, city="NYC")
# name: Alice
# age: 30
# city: NYC
Combine everything
def flexible_function(required, *args, default=10, **kwargs):
print(f"Required: {required}")
print(f"Extra positional: {args}")
print(f"Default: {default}")
print(f"Extra keyword: {kwargs}")
flexible_function(1, 2, 3, default=20, x=100, y=200)
Required: 1
Extra positional: (2, 3)
Default: 20
Extra keyword: {'x': 100, 'y': 200}
def get_stats(numbers):
return min(numbers), max(numbers), sum(numbers) / len(numbers)
Unpack returned values
minimum, maximum, average = get_stats([10, 20, 30, 40])
print(f"Min: {minimum}, Max: {maximum}, Avg: {average}")
Or keep as tuple
stats = get_stats([5, 10, 15])
print(stats) # (5, 15, 10.0)def validate_age(age):
if age < 0:
return "Invalid age"
if age < 18:
return "Minor"
if age < 65:
return "Adult"
return "Senior"
print(validate_age(25)) # Adult
print(validate_age(-5)) # Invalid agedef greet(name):
print(f"Hello, {name}!")
# No return statement = returns None
result = greet("Alice") # Prints: Hello, Alice!
print(result) # NoneAnonymous functions for simple operations:
# Regular function
def double(x):
return x * 2
Lambda equivalent
double = lambda x: x * 2
print(double(5)) # 10
With multiple parameters
add = lambda a, b: a + b
print(add(3, 4)) # 7
Typically used inline
numbers = [1, 2, 3, 4, 5]
doubled = list(map(lambda x: x * 2, numbers))
print(doubled) # [2, 4, 6, 8, 10]When to use lambdas:
numbers = [1, 2, 3, 4, 5]
Using map
squared = list(map(lambda x: x**2, numbers))
print(squared) # [1, 4, 9, 16, 25]
Equivalent list comprehension (preferred in Python)
squared = [x**2 for x in numbers]
Map with named function
def celsius_to_fahrenheit(c):
return (c * 9/5) + 32
temps_c = [0, 10, 20, 30]
temps_f = list(map(celsius_to_fahrenheit, temps_c))
print(temps_f) # [32.0, 50.0, 68.0, 86.0]numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Using filter
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # [2, 4, 6, 8, 10]
Equivalent list comprehension (preferred)
evens = [x for x in numbers if x % 2 == 0]
Filter with named function
def is_adult(age):
return age >= 18
ages = [12, 25, 17, 30, 16, 22]
adults = list(filter(is_adult, ages))
print(adults) # [25, 30, 22]from functools import reduce
numbers = [1, 2, 3, 4, 5]
Sum all numbers
total = reduce(lambda acc, x: acc + x, numbers)
print(total) # 15
Find maximum
maximum = reduce(lambda a, b: a if a > b else b, numbers)
print(maximum) # 5
More readable: use built-in functions
total = sum(numbers)
maximum = max(numbers)Decorators modify or enhance functions:
def uppercase_decorator(func):
"""Decorator that converts result to uppercase."""
def wrapper():
result = func()
return result.upper()
return wrapper
@uppercase_decorator
def greet():
return "hello, world!"
print(greet()) # HELLO, WORLD!def timer(func):
"""Measure function execution time."""
import time
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end - start:.4f}s")
return result
return wrapper
@timer
def slow_function(n):
total = 0
for i in range(n):
total += i
return total
result = slow_function(1000000)
# slow_function took 0.0523sdef bold(func):
def wrapper():
return f"<b>{func()}</b>"
return wrapper
def italic(func):
def wrapper():
return f"<i>{func()}</i>"
return wrapper
@bold
@italic
def greet():
return "Hello"
print(greet()) # <b><i>Hello</i></b># Cache results
def memoize(func):
cache = {}
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
@memoize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
Validate inputs
def validate_positive(func):
def wrapper(x):
if x <= 0:
raise ValueError("Must be positive")
return func(x)
return wrapper
@validate_positive
def square_root(x):
return x ** 0.5
Log function calls
def log_calls(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with {args} {kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned {result}")
return result
return wrapper
@log_calls
def add(a, b):
return a + bFunctions are objects - can be passed around:
def add(a, b):
return a + b
def subtract(a, b):
return a - b
def apply_operation(func, x, y):
"""Apply any function to two numbers."""
return func(x, y)
result1 = apply_operation(add, 10, 5) # 15
result2 = apply_operation(subtract, 10, 5) # 5Store functions in data structures
operations = {
"add": add,
"subtract": subtract,
"multiply": lambda a, b: a * b
}
op = operations["add"]
print(op(3, 4)) # 7Functions that remember values from enclosing scope:
def make_multiplier(factor):
"""Create a function that multiplies by factor."""
def multiply(x):
return x * factor # 'factor' is remembered
return multiply
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
Practical example: create validators
def make_validator(min_val, max_val):
def validate(value):
return min_val <= value <= max_val
return validate
is_valid_age = make_validator(0, 120)
is_valid_percentage = make_validator(0, 100)
print(is_valid_age(25)) # True
print(is_valid_age(150)) # False
print(is_valid_percentage(50)) # TrueFunctions calling themselves:
def factorial(n):
"""Calculate n! recursively."""
if n <= 1:
return 1
return n * factorial(n - 1)
print(factorial(5)) # 120
Countdown example
def countdown(n):
if n <= 0:
print("Blast off!")
else:
print(n)
countdown(n - 1)
countdown(5)
# 5
# 4
# 3
# 2
# 1
Blast off!
Directory tree traversal
import os
def list_files(path, indent=0):
for item in os.listdir(path):
print(" " * indent + item)
item_path = os.path.join(path, item)
if os.path.isdir(item_path):
list_files(item_path, indent + 1)*args and **kwargs for flexible parameters