🐍

Parameters and Arguments

Programare Python Intermediate 5 min read

Parameters and Arguments in Python

Terminology

  • Parameter: Variable in the function definition
  • Argument: Value passed at call time
def greet(name):    # 'name' is PARAMETER
    print(f"Hello, {name}!")

greet("Ana")        # "Ana" is ARGUMENT

Positional Arguments

Match parameters in the order given:

def introduce(name, age, city):
    print(f"{name}, {age} years old, from {city}")

# Order matters!
introduce("Ana", 20, "NYC")       # Ana, 20 years old, from NYC
introduce(20, "Ana", "NYC")       # 20, Ana years old, from NYC (wrong!)

Keyword Arguments (named)

Explicitly specify the parameter name:

def introduce(name, age, city):
    print(f"{name}, {age} years old, from {city}")

# Order doesn't matter
introduce(age=20, city="NYC", name="Ana")  # Ana, 20 years old, from NYC

# Combination - positional BEFORE keyword
introduce("Ana", city="NYC", age=20)  # Ana, 20 years old, from NYC

Default Values

Parameters can have preset values:

def greet(name, greeting="Hello"):
    print(f"{greeting}, {name}!")

greet("Ana")                    # Hello, Ana!
greet("Ana", "Good day")        # Good day, Ana!
greet("Ana", greeting="Ciao")   # Ciao, Ana!

Parameter order:

Parameters with default values must be after those without:

# CORRECT
def func(required, optional="default"):
    pass

# WRONG - SyntaxError
def func(optional="default", required):
    pass

WARNING - Mutable values as default:

# WRONG - classic bug!
def add(element, lst=[]):
    lst.append(element)
    return lst

print(add(1))  # [1]
print(add(2))  # [1, 2] - Not [2]!
print(add(3))  # [1, 2, 3]

# CORRECT
def add(element, lst=None):
    if lst is None:
        lst = []
    lst.append(element)
    return lst

*args - Variable Positional Arguments

*args collects extra positional arguments into a tuple:

def total(*numbers):
    print(type(numbers))  # <class 'tuple'>
    result = 0
    for n in numbers:
        result += n
    return result

print(total(1, 2, 3))        # 6
print(total(1, 2, 3, 4, 5))  # 15
print(total())               # 0

Combining with normal parameters:

def introduce(title, *names):
    print(f"{title}:")
    for n in names:
        print(f"  - {n}")

introduce("Students", "Ana", "Bob", "Chris")
# Students:
#   - Ana
#   - Bob
#   - Chris

**kwargs - Variable Keyword Arguments

**kwargs collects extra keyword arguments into a dictionary:

def info(**data):
    print(type(data))  # <class 'dict'>
    for key, value in data.items():
        print(f"{key}: {value}")

info(name="Ana", age=20, city="NYC")
# name: Ana
# age: 20
# city: NYC

Combining with other parameters:

def profile(name, **details):
    print(f"Name: {name}")
    for k, v in details.items():
        print(f"  {k}: {v}")

profile("Ana", age=20, job="student")
# Name: Ana
#   age: 20
#   job: student

Parameter Order

When combining all types, the order is:

  1. Required positional parameters
  2. *args
  3. Keyword-only parameters (after *)
  4. **kwargs
def func(a, b, *args, c=10, **kwargs):
    print(f"a={a}, b={b}")
    print(f"args={args}")
    print(f"c={c}")
    print(f"kwargs={kwargs}")

func(1, 2, 3, 4, c=20, x=30, y=40)
# a=1, b=2
# args=(3, 4)
# c=20
# kwargs={'x': 30, 'y': 40}

Unpacking Arguments

Unpacking list with *:

def total(a, b, c):
    return a + b + c

numbers = [1, 2, 3]
print(total(*numbers))  # 6 (equivalent to total(1, 2, 3))

Unpacking dictionary with **:

def introduce(name, age, city):
    print(f"{name}, {age}, {city}")

data = {"name": "Ana", "age": 20, "city": "NYC"}
introduce(**data)  # Ana, 20, NYC

Keyword-Only Parameters

Parameters after * can only be passed as keyword:

def func(a, b, *, c, d):
    print(a, b, c, d)

# Correct
func(1, 2, c=3, d=4)

# Wrong
# func(1, 2, 3, 4)  # TypeError

Positional-Only Parameters (Python 3.8+)

Parameters before / can only be passed positionally:

def func(a, b, /, c, d):
    print(a, b, c, d)

# Correct
func(1, 2, 3, 4)
func(1, 2, c=3, d=4)

# Wrong
# func(a=1, b=2, c=3, d=4)  # TypeError

Common Patterns

Function with multiple options:

def connect(host, port=3306, **options):
    print(f"Connecting to {host}:{port}")
    for opt, val in options.items():
        print(f"  {opt}={val}")

connect("localhost")
connect("db.server.com", 5432, user="admin", ssl=True)

Generic wrapper:

def log_call(function):
    def wrapper(*args, **kwargs):
        print(f"Call: {function.__name__}")
        return function(*args, **kwargs)
    return wrapper

Common Mistakes

1. Wrong order when calling

def f(a, b):
    pass

# f(a=1, 2)  # SyntaxError - keyword before positional!
f(1, b=2)    # Correct

2. Mutable value as default

# WRONG
def f(lst=[]):
    lst.append(1)
    return lst

# CORRECT
def f(lst=None):
    if lst is None:
        lst = []
    lst.append(1)
    return lst

3. Confusion *args and **kwargs

def f(*args, **kwargs):
    print(args)    # tuple
    print(kwargs)  # dict

f(1, 2, a=3, b=4)
# (1, 2)
# {'a': 3, 'b': 4}

Key Points for Exam

  • Positional parameters: order matters
  • Keyword parameters: order doesn’t matter
  • Default values: required parameters first
  • *args: tuple with extra positional arguments
  • **kwargs: dict with extra keyword arguments
  • NEVER use list/dict as mutable default value!
  • Unpacking: *list, **dictionary

Review Questions

  1. What’s the difference between parameter and argument?
  2. What data type is *args? And **kwargs?
  3. Why don’t we use lst=[] as default?
  4. In what order do we place parameters?

📚 Related Articles