Function Arguments
There are several ways to pass arguments to a function: positional arguments, keyword arguments, and default arguments.
Positional Arguments
Positional arguments are passed based on their position (order) in the function call. They must be provided in the same order as the parameters:
def describe_pet(animal_type, pet_name): print(f"I have a {animal_type} named {pet_name}.")
describe_pet("dog", "Buddy") # I have a dog named Buddy.describe_pet("cat", "Whiskers") # I have a cat named Whiskers.The order matters! Swapping the arguments will produce incorrect results:
describe_pet("Buddy", "dog") # I have a Buddy named dog. (Wrong!)Keyword Arguments
We can also have keyword arguments, where the user can supply key=value. The order of the arguments does not matter:
def greeting(firstname, lastname, age): print(f"Welcome {firstname} {lastname}, you are {age} years old!")
greeting(age=10, firstname="Alice", lastname="Brown")# Welcome Alice Brown, you are 10 years old!Mixing Positional and Keyword Arguments
You can mix positional and keyword arguments, but positional arguments must come before keyword arguments:
def describe_pet(animal_type, pet_name, age): print(f"I have a {age}-year-old {animal_type} named {pet_name}.")
describe_pet("dog", pet_name="Buddy", age=5)describe_pet("cat", age=3, pet_name="Whiskers")Warning
You cannot use a positional argument after a keyword argument:
describe_pet(animal_type="dog", "Buddy", age=5) # SyntaxErrorDefault Arguments
A default argument in a function is an argument in which a programmer is not required to specify. If it wasn’t specified, then this argument is given a default value.
Any number of arguments in a function can have a default value. But once we have a default argument, all the arguments to its right must also have default values. This means to say, non-default arguments cannot follow default arguments.
def greet(name, msg="Good morning!"): print("Hello", name + ', ' + msg)
greet("Jana")# Hello Jana, Good morning!
greet("Sally", "How do you do?")# Hello Sally, How do you do?Multiple Arguments
Functions can accept any number of positional or keyword arguments using special syntax:
Arbitrary Positional Arguments (*args)
Python supports arbitrary arguments by appending one asterisk before the argument name in the function definition. Then all of the passed values will be stored in a tuple:
def sum_numbers(*args): total = 0 for i in args: total += i return total
print(sum_numbers(1, 2, 3, 4)) # 10Arbitrary Keyword Arguments (**kwargs)
If we do not know how many keyword arguments the user would pass, we could add two asterisks before the variable name in the function definition. Then all of the passed keyword arguments will be stored in a dictionary:
def log_message(message, **kwargs): print(f"Log: {message}") if kwargs: print("Additional metadata") for key, value in kwargs.items(): print(f"{key}: {value}")
log_message("Error occurred", severity="high", timestamp="2023-01-05 10:30:00")# Log: Error occurred# Additional metadata# severity: high# timestamp: 2023-01-05 10:30:00
log_message("User logged in", user_id=123, ip_address="192.168.0.1")# Log: User logged in# Additional metadata# user_id: 123# ip_address: 192.168.0.1Combining *args and **kwargs
You can use both together, but *args must come before **kwargs:
def function(*args, **kwargs): print("Positional:", args) print("Keyword:", kwargs)
function(1, 2, 3, name="Alice", age=25)# Positional: (1, 2, 3)# Keyword: {'name': 'Alice', 'age': 25}Argument Unpacking
You can unpack sequences and dictionaries when calling functions:
def greet(name, age, city): print(f"{name}, {age}, from {city}")
person = ("Alice", 25, "New York")greet(*person) # Unpacking tuple
info = {"name": "Bob", "age": 30, "city": "Boston"}greet(**info) # Unpacking dictionaryPosition-Only Arguments
We can force the user to enter positional-only arguments instead of keyword arguments by listing the positional arguments first, then add a / at the end:
def test(x, y, /, z, t=0): print(x, y, z, t)
test(1, 2, 3, 4) # 1 2 3 4test(1, 2, 3) # 1 2 3 0test(1, 2, t=4, z=3) # 1 2 3 4test(y=1, x=2, z=3, t=4) # TypeErrortest(t=1, z=2, x=3, y=4) # TypeErrortest(x=1, y=2, z=3, t=4) # TypeErrorIf all arguments are position-only:
def test(x, y, /): print(x, y)
test(1, 2) # 1 2Keyword-Only Arguments
We can force the user to enter keyword-only arguments instead of positional arguments by adding an asterisk, followed by the arguments:
def test(x, y, *, z, t): print(x, y, z, t)
test(1, 2, z=3, t=4) # 1 2 3 4test(1, 2, t=4, z=3) # 1 2 3 4test(x=1, y=2, z=3, t=4) # 1 2 3 4test(1, 2, 3, 4) # TypeErrorIf all arguments are keyword-only:
def test(*, z, t): print(z, t)
test(z=3, t=4) # 3 4test(t=4, z=3) # 3 4Combining Position-Only and Keyword-Only Arguments
We can combine positional-only and keyword-only arguments by having the / and * in the argument list. Any argument before the / is positional-only. Any argument after the * is keyword-only. The / is always before the * in the argument list:
def test(x, y, /, *, z, t): print(x, y, z, t)
test(1, 2, z=3, t=4) # 1 2 3 4test(1, 2, t=4, z=3) # 1 2 3 4Default Values with Argument Types
You can combine default values with position-only and keyword-only arguments:
def calculate( x, y, # Position-only, required /, operation="add", # Regular with default *, round_result=False # Keyword-only with default): if operation == "add": result = x + y elif operation == "multiply": result = x * y else: result = x - y
if round_result: return round(result) return result
print(calculate(5, 3)) # 8print(calculate(5, 3, "multiply")) # 15print(calculate(5, 3, round_result=True)) # 8print(calculate(5, 3, "multiply", round_result=True)) # 15Real-World Examples
Many built-in functions use these patterns:
def process_data( data, # Position-only: the main data /, format="json", # Regular: common option *, validate=True, # Keyword-only: advanced option cache=False, # Keyword-only: advanced option timeout=30 # Keyword-only: advanced option): """Process data with various options""" # Implementation here pass
# Clear and explicit callsprocess_data(my_data)process_data(my_data, format="xml")process_data(my_data, format="json", validate=False, cache=True)Exercises
Exercise 1: Default Arguments
Write a function called greet that takes a name and an optional greeting message. If no message is provided, it should default to “Good morning!”. Call it with and without the message argument.
def greet(name, msg="Good morning!"): print("Hello", name + ', ' + msg)
greet("Jana")greet("Sally", "How do you do?")Exercise 2: Keyword Arguments
Write a function called create_profile that takes firstname, lastname, and age as parameters. Call it using keyword arguments in a different order than the parameter definition.
def create_profile(firstname, lastname, age): print(f"{firstname} {lastname}, age {age}")
create_profile(age=25, lastname="Smith", firstname="John")Exercise 3: Arbitrary Arguments (*args)
Write a function called calculate_average that takes any number of numbers using *args and returns their average. Read the number of values first, then read that many numbers.
Calculate Average with *args
def calculate_average(*args): if len(args) == 0: return 0 return sum(args) / len(args)
n = int(input())numbers = []for i in range(n): numbers.append(int(input()))
result = calculate_average(*numbers)print(result)Exercise 4: Arbitrary Keyword Arguments (**kwargs)
Write a function called print_info that takes a required title parameter and any number of keyword arguments. Print the title, then print all keyword arguments as “key: value” pairs.
def print_info(title, **kwargs): print(f"Title: {title}") for key, value in kwargs.items(): print(f"{key}: {value}")
print_info("User Profile", name="Alice", age=25, city="New York")Exercise 5: Mixing Argument Types
What will be the output of the following code?
def test(a, b=2, *args, **kwargs): print(f"a={a}, b={b}") print(f"args={args}") print(f"kwargs={kwargs}")
test(1, 3, 4, 5, x=10, y=20)a=1, b=3args=(4, 5)kwargs={'x': 10, 'y': 20}Exercise 6: Position-Only Arguments
Write a function called divide that takes two position-only arguments x and y, and returns x / y. Read two numbers from input and call the function with positional arguments.
Position-Only Arguments
def divide(x, y, /): return x / y
x = int(input())y = int(input())result = divide(x, y)print(result)Exercise 7: Keyword-Only Arguments
Write a function called create_user that takes username as a regular argument, and email and age as keyword-only arguments. Call it correctly and incorrectly.
def create_user(username, *, email, age): print(f"User: {username}, Email: {email}, Age: {age}")
create_user("alice", email="alice@example.com", age=25) # Correct
# create_user("alice", "alice@example.com", 25) # TypeErrorExercise 8: Combining Position-Only and Keyword-Only
Write a function called calculate that has two position-only arguments x and y, and two keyword-only arguments operation (default “add”) and round_result (default False). The function should perform the operation and optionally round the result.
def calculate(x, y, /, *, operation="add", round_result=False): if operation == "add": result = x + y elif operation == "multiply": result = x * y else: result = x - y
if round_result: return round(result) return result
print(calculate(5, 3, operation="multiply", round_result=True)) # 15Exercise 9: Understanding Argument Order
What will happen when we try to call this function? Explain why.
def test(x, y, /, z, *, w): print(x, y, z, w)
test(1, 2, 3, w=4) # Will this work?test(1, 2, z=3, w=4) # Will this work?test(x=1, y=2, z=3, w=4) # Will this work?test(1, 2, 3, w=4) # Works: 1 2 3 4test(1, 2, z=3, w=4) # Works: 1 2 3 4test(x=1, y=2, z=3, w=4) # TypeError: x and y are position-only