Functions are Python's building blocks. Here's everything you need to know.
Basic Functions
def greet(name):
return f"Hello, {name}!"
result = greet("Owen")Arguments
Positional and Keyword
def create_user(name, email, admin=False):
return {"name": name, "email": email, "admin": admin}
# Positional
create_user("Owen", "owen@example.com")
# Keyword
create_user(name="Owen", email="owen@example.com")
# Mixed
create_user("Owen", email="owen@example.com", admin=True)*args and **kwargs
def func(*args, **kwargs):
print(f"args: {args}") # Tuple of positional args
print(f"kwargs: {kwargs}") # Dict of keyword args
func(1, 2, 3, name="Owen", age=25)
# args: (1, 2, 3)
# kwargs: {'name': 'Owen', 'age': 25}Unpacking Arguments
args = [1, 2, 3]
kwargs = {"name": "Owen"}
func(*args, **kwargs)
# Same as: func(1, 2, 3, name="Owen")Keyword-Only Arguments
def func(a, b, *, keyword_only):
pass
func(1, 2, keyword_only=3) # OK
func(1, 2, 3) # TypeErrorPositional-Only Arguments (3.8+)
def func(positional_only, /, normal):
pass
func(1, 2) # OK
func(1, normal=2) # OK
func(positional_only=1, normal=2) # TypeErrorReturn Values
# Single value
def square(x):
return x ** 2
# Multiple values (tuple)
def divide(a, b):
return a // b, a % b
quotient, remainder = divide(10, 3)
# No return = None
def no_return():
pass
result = no_return() # NoneDefault Arguments
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
# Warning: mutable defaults are shared!
def bad(items=[]): # Don't do this
items.append(1)
return items
bad() # [1]
bad() # [1, 1] - same list!
# Use None instead
def good(items=None):
if items is None:
items = []
items.append(1)
return itemsLambda Functions
# Anonymous functions
square = lambda x: x ** 2
add = lambda x, y: x + y
# Common use: sorting
users = [{"name": "Bob"}, {"name": "Alice"}]
sorted(users, key=lambda u: u["name"])
# With filter/map
numbers = [1, 2, 3, 4, 5]
evens = list(filter(lambda x: x % 2 == 0, numbers))
squares = list(map(lambda x: x ** 2, numbers))Use lambda for simple, one-time functions. Otherwise define a regular function.
First-Class Functions
Functions are objects:
def greet(name):
return f"Hello, {name}!"
# Assign to variable
say_hello = greet
say_hello("Owen") # "Hello, Owen!"
# Pass as argument
def apply(func, value):
return func(value)
apply(greet, "Owen")
# Return from function
def make_greeter(greeting):
def greeter(name):
return f"{greeting}, {name}!"
return greeter
hi = make_greeter("Hi")
hi("Owen") # "Hi, Owen!"Closures
Inner functions that remember outer scope:
def counter():
count = 0
def increment():
nonlocal count # Access outer variable
count += 1
return count
return increment
c = counter()
c() # 1
c() # 2
c() # 3Decorators
Functions that modify functions:
def log(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
@log
def greet(name):
return f"Hello, {name}!"
greet("Owen")
# Calling greet
# Hello, Owen!Docstrings
def calculate_area(radius):
"""Calculate the area of a circle.
Args:
radius: The radius of the circle.
Returns:
The area of the circle.
Raises:
ValueError: If radius is negative.
"""
if radius < 0:
raise ValueError("Radius must be non-negative")
return 3.14159 * radius ** 2
# Access docstring
print(calculate_area.__doc__)Type Hints
def greet(name: str) -> str:
return f"Hello, {name}!"
def process(items: list[int]) -> dict[str, int]:
return {"sum": sum(items), "count": len(items)}Recursion
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
# With memoization
from functools import lru_cache
@lru_cache
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)Best Practices
Keep functions small:
# Do one thing well
def validate_email(email: str) -> bool:
return "@" in email and "." in emailUse descriptive names:
# Bad
def f(x): ...
# Good
def calculate_tax(amount): ...Avoid side effects:
# Bad - modifies global state
total = 0
def add(x):
global total
total += x
# Good - pure function
def add(a, b):
return a + bReturn early:
def process(data):
if not data:
return None
if not valid(data):
return None
# Main logic here
return resultFunctions are the heart of Python. Master them.
React to this post: