Functions
Functions are reusable blocks of code that can accept parameters and return values.
Function Definition
def function_name(param1, param2):
# function body
return resultExamples
# Simple function
def greet(name):
print("Hello, " + name)
# Function with return
def add(a, b):
return a + b
# Recursive function
def factorial(n):
if n <= 1:
return 1
else:
return n * factorial(n - 1)
# No parameters
def get_pi():
return 3.14159
# No return (returns None)
def log_message(msg):
print("[LOG]", msg)Default Parameters
def greet(name, greeting="Hello", punctuation="!"):
return greeting + ", " + name + punctuation
# Using defaults
greet("World") # "Hello, World!"
# Override defaults
greet("Alice", "Hi") # "Hi, Alice!"
greet("Bob", "Hey", ".") # "Hey, Bob."Keyword Arguments
Functions can be called with keyword arguments, which can be mixed with positional arguments:
def greet(name, greeting="Hello", punctuation="!"):
return greeting + ", " + name + punctuation
# Positional arguments
greet("World") # "Hello, World!"
# Keyword arguments
greet(name="Alice") # "Hello, Alice!"
greet(greeting="Hi", name="Bob") # "Hi, Bob!"
# Mixed positional and keyword
greet("Charlie", greeting="Hey") # "Hey, Charlie!"
greet("Diana", punctuation=".") # "Hello, Diana."
# All keyword arguments (order doesn't matter)
greet(punctuation="?", name="Eve", greeting="Howdy") # "Howdy, Eve?"Rules
- Positional arguments must come before keyword arguments
- Each parameter can only be specified once
- Keyword arguments work with default parameter values
Variadic Arguments (*args)
Functions can accept a variable number of positional arguments:
def sum_all(*args):
total = 0
for num in args:
total += num
return total
print(sum_all(1, 2, 3)) # 6
print(sum_all(1, 2, 3, 4)) # 10
print(sum_all()) # 0Mixing with Regular Parameters
def log(level, *messages):
prefix = "[" + level + "] "
for msg in messages:
print(prefix + str(msg))
log("INFO", "System started", "Ready")
# Output:
# [INFO] System started
# [INFO] Ready
log("ERROR", "Failed to connect", "Retrying...", "Attempt 2")Note: *args must come after regular parameters and default parameters.
Keyword Arguments Collection (**kwargs)
Functions can accept arbitrary keyword arguments:
def test_kwargs(**kwargs):
return kwargs
result = test_kwargs(a=1, b=2, c=3)
print(result) # {"a": 1, "b": 2, "c": 3}Combining All Parameter Types
def func_with_all(a, b=10, *args, **kwargs):
print("a:", a)
print("b:", b)
print("args:", args)
print("kwargs:", kwargs)
func_with_all(1, 2, 3, 4, x=5, y=6)
# Output:
# a: 1
# b: 2
# args: [3, 4]
# kwargs: {"x": 5, "y": 6}Parameter Order
When using multiple parameter types, they must appear in this order:
- Regular parameters (e.g.,
a,b) - Default parameters (e.g.,
c=10) - Variadic arguments (
*args) - Keyword arguments (
**kwargs)
Argument Unpacking
Positional Argument Unpacking (*)
Unpack a list or tuple into individual arguments:
def sum_three(a, b, c):
return a + b + c
numbers = [1, 2, 3]
result = sum_three(*numbers) # Same as sum_three(1, 2, 3)
print(result) # 6
# Unpacking tuples
coords = (10, 20)
def add_coords(x, y):
return x + y
print(add_coords(*coords)) # 30
# Partial unpacking
result = sum_three(10, *numbers[1:]) # Same as sum_three(10, 2, 3)Keyword Argument Unpacking (**)
Unpack a dictionary into keyword arguments:
def create_user(name, age, active=True):
return {"name": name, "age": age, "active": active}
user_data = {"name": "Alice", "age": 30}
user = create_user(**user_data)
print(user) # {"name": "Alice", "age": 30, "active": True}
# Override with additional kwargs
user = create_user(**user_data, active=False)
print(user) # {"name": "Alice", "age": 30, "active": False}Combining Both
def func_with_all(a, b, *args, **kwargs):
return {"a": a, "b": b, "args": args, "kwargs": kwargs}
args = [1, 2, 3, 4]
kwargs = {"x": 10, "y": 20}
result = func_with_all(*args, **kwargs)
# {"a": 1, "b": 2, "args": [3, 4], "kwargs": {"x": 10, "y": 20}}Decorators
Decorators wrap a function with another callable using @ syntax:
def double_result(fn):
def wrapper(*args):
return fn(*args) * 2
return wrapper
@double_result
def add(a, b):
return a + b
print(add(3, 4)) # 14Decorators stack — applied bottom-up (innermost first):
@outer
@inner
def fn(): ...
# equivalent to: fn = outer(inner(fn))See Classes for @property and @staticmethod.
Lambda Functions
Anonymous functions for simple operations:
# Basic lambda
square = lambda x: x * x
print(square(5)) # 25
# Multiple parameters
add = lambda a, b: a + b
print(add(3, 4)) # 7
# With default parameters
greet = lambda name, greeting="Hello": greeting + ", " + name
print(greet("World")) # "Hello, World"
# In sorted
sorted(["ccc", "a", "bb"], key=lambda s: len(s)) # ["a", "bb", "ccc"]
# In map
list(map(lambda x: x * 2, [1, 2, 3])) # [2, 4, 6]
# In filter
list(filter(lambda x: x > 2, [1, 2, 3, 4])) # [3, 4]Return Statement
# Return value
def add(a, b):
return a + b
# Return None (explicit)
def do_nothing():
return None
# Return None (implicit)
def log(msg):
print(msg)
# No return statement = returns None
# Multiple returns
def classify(n):
if n < 0:
return "negative"
elif n == 0:
return "zero"
else:
return "positive"Closures
Functions can capture variables from their enclosing scope:
def make_counter(start=0):
count = [start] # Use list to allow modification
def counter():
count[0] += 1
return count[0]
return counter
counter = make_counter(10)
print(counter()) # 11
print(counter()) # 12
print(counter()) # 13Multiple Assignment
Unpack sequences into multiple variables:
# Basic unpacking
x, y = [1, 2]
a, b, c = (10, 20, 30)
# Extended unpacking with *
first, *middle, last = [1, 2, 3, 4, 5]
# first=1, middle=[2,3,4], last=5
*head, tail = [1, 2, 3]
# head=[1,2], tail=3
head, *tail = [1, 2, 3]
# head=1, tail=[2,3]
# With minimal elements
a, *b, c = [1, 2]
# a=1, b=[], c=2
# In loops
for first, *rest in [[1,2,3], [4,5,6,7]]:
print(f"first={first}, rest={rest}")See Also
- Control Flow - Loops and conditionals
- Error Handling - Try/except in functions
- Classes - Methods and class functions