Understanding how to pass information into functions is key to writing flexible, reusable code. In this lesson, you’ll learn the difference between parameters (names in a function definition) and arguments (actual values you pass when calling the function), plus how to use positional, keyword, and default values — and a quick intro to *args, **kwargs, and unpacking.
Parameters vs Arguments
A parameter is the variable in the function definition. An argument is the value you supply when calling the function.
def greet(name): # 'name' is a parameter
print(f"Hello, {name}!")
greet("Tyler") # "Tyler" is an argument
Positional Arguments
Arguments matched by position are the most common — the first argument goes to the first parameter, and so on.
def report_city(name, city):
print(f"{name} lives in {city}.")
report_city("Maya", "Sydney") # positional match
Keyword Arguments
Keyword arguments match by name=value, letting you pass values in any order and making calls self-documenting.
def make_account(username, role):
print(f"User: {username}, Role: {role}")
make_account(role="student", username="tyler") # order swapped, still works
Default Parameter Values
Give parameters default values so callers can omit them. Defaults are evaluated once (see pitfall below).
def shout(msg, times=1, upper=True):
if upper:
msg = msg.upper()
for i in range(times):
print(msg)
shout("nice work") # uses defaults
shout("ok", 2, False) # positional
shout(msg="custom", upper=False) # keywords
Mixing Positional and Keyword
You can mix them — but positional arguments must come first.
def connect(host, port, secure=True):
print(f"host={host}, port={port}, secure={secure}")
connect("example.com", 443, secure=True) # OK
# connect(host="example.com", 443) ← Error: positional after keyword
Unpacking with * and **
Use * to unpack a sequence into positional arguments, and ** to unpack a dict into keyword arguments.
def area(w, h):
return w * h
dims = (3, 5)
print(area(*dims)) # 15
opts = {"w": 4, "h": 7}
print(area(**opts)) # 28
Variable-length Parameters: *args and **kwargs
Capture “any number” of positional or keyword arguments in your function.
def summarize(*args, **kwargs):
print("Positional:", args)
print("Keywords:", kwargs)
summarize(1, 2, 3, mode="fast", retry=2)
Common Pitfall: Mutable Defaults
Never use a mutable object (like a list or dict) as a default parameter. It persists across calls.
# Bad: shared list used across calls
def append_item(item, bucket=[]):
bucket.append(item)
return bucket
print(append_item("a")) # ['a']
print(append_item("b")) # ['a','b'] ← unexpected carry-over
Fix it by using None and creating a new object inside the function.
def append_item(item, bucket=None):
if bucket is None:
bucket = []
bucket.append(item)
return bucket
print(append_item("a")) # ['a']
print(append_item("b")) # ['b']
Interactive Exercise: Parameters & Arguments Playground
Experiment with positional args, keyword args, defaults, and unpacking. Then try adding *args / **kwargs support.
# 1) Define a function with defaults
def format_name(first, last, middle="", title=""):
parts = [title, first, middle, last]
return " ".join([p for p in parts if p])
print(format_name("Tyler", "Connor")) # positional
print(format_name(first="Maya", last="Lee", title="Dr.")) # keywords
# 2) Mix positional + keyword (positional first)
print(format_name("Alex", "Kim", title="Mr"))
# 3) Unpack a tuple and dict into a call
t = ("Ada", "Lovelace")
print(format_name(*t))
d = {"first": "Grace", "last": "Hopper", "title": "Rear Adm."}
print(format_name(**d))
# 4) Your turn:
# - Add *args to collect extra name parts and include them in the output.
# - Add **kwargs to accept optional fields like 'suffix' and append them.
* and ** to unpack.None and create the list/dict inside the function.Mastering parameters and arguments lets you design clear, flexible APIs for your functions — and makes your code far easier to read, test, and reuse.