Function parameters define how data flows into a function when it’s called. Python offers rich flexibility: positional arguments (order-based), keyword arguments (name-based), defaults, variable args (*args, **kwargs), positional-only (/), and keyword-only (*) parameters. Mastering them lets you create intuitive, safe, and powerful function APIs.
In 2026, modern Python favors explicit signatures with type hints, meaningful defaults, and clear separation of positional and keyword args. Here’s a complete, practical guide to using all parameter types effectively.
1. Positional Parameters (Order-Based)
def add_numbers(x: int, y: int) -> int:
"""Add two integers."""
return x + y
# Positional call — arguments matched by position
print(add_numbers(3, 4)) # 7
# Error if order wrong or count mismatches
# add_numbers(3) # TypeError: missing 1 required positional argument
2. Keyword (Named) Parameters (Order-Independent)
def greet(name: str, greeting: str = "Hello") -> str:
return f"{greeting}, {name}!"
# Keyword calls — explicit and order doesn't matter
print(greet(name="Alice")) # Hello, Alice!
print(greet(greeting="Hi", name="Bob")) # Hi, Bob!
# Mix positional + keyword (positional must come first!)
print(greet("Charlie", greeting="Hey")) # Hey, Charlie!
3. Default Parameters (Optional Inputs)
def calculate_total(price: float, tax_rate: float = 0.08, discount: float = 0.0) -> float:
"""Calculate price with optional tax and discount."""
subtotal = price * (1 - discount)
return round(subtotal * (1 + tax_rate), 2)
print(calculate_total(100)) # 108.0
print(calculate_total(100, discount=0.1)) # 97.2
**Critical rule**: Never use mutable defaults (lists, dicts, etc.) — they are shared across calls!
```python
# BAD — mutable default is shared!
def bad_append(item, lst=[]):
lst.append(item)
return lst
print(bad_append(1)) # [1]
print(bad_append(2)) # [1, 2] ? unexpected!
# Correct pattern
def good_append(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst