When defining a function, you may not know the number of arguments in advance. For example, you might need a function that sums up multiple numbers or outputs multiple log messages at once. In Python, adding an asterisk (*) before an argument name allows you to accept an arbitrary number (0 or more) of arguments. This is called variable-length positional arguments.
This article explains the basic usage of *args and the rules for combining it with normal arguments.
What are Variable-Length Positional Arguments (*args)?
When you add * to an argument in a function definition, that argument receives “all remaining positional arguments” as a tuple. By convention, the variable name args (short for arguments) is used, but you can use any name like *items or *values. The important part is the leading *.
Basic Usage
As a specific example, let’s create a function that accepts a server name and multiple event messages to record. The number of messages can vary with each call.
def print_server_log(server_name, *messages):
"""
Function to accept a server name and any number of messages.
"""
print(f"--- Server: {server_name} ---")
# messages is received as a tuple
print(f"Number of messages: {len(messages)}")
print(f"Type: {type(messages)}")
if not messages:
print(" (No messages)")
else:
for msg in messages:
print(f" - {msg}")
# --- Calling the function ---
# 1. Mandatory argument only, no variable-length arguments
print_server_log("Web-Server-01")
print("\n")
# 2. Mandatory argument + multiple messages
print_server_log("DB-Server-01", "Connection Start", "Execute Query", "Disconnection")
Output:
--- Server: Web-Server-01 ---
Number of messages: 0
Type: <class 'tuple'>
(No messages)
--- Server: DB-Server-01 ---
Number of messages: 3
Type: <class 'tuple'>
- Connection Start
- Execute Query
- Disconnection
*messages becomes a tuple ('Connection Start', 'Execute Query', 'Disconnection') containing the passed arguments. If no arguments are passed, it becomes an empty tuple ().
Rules Regarding Argument Order
There is a strict rule regarding the order of arguments when using variable-length positional arguments. *args must be defined after normal positional arguments (mandatory arguments). This is because Python assigns arguments in order from the beginning, and stores the remainder in *args.
Correct Order
# OK: Mandatory argument first, variable-length argument second
def calculate_total(tax_rate, *prices):
total = sum(prices)
return int(total * (1 + tax_rate))
# Calculate total for 100, 200, 300 with 10% tax (0.1)
print(calculate_total(0.1, 100, 200, 300))
Incorrect Order
# NG: If the variable-length argument comes first, there is no way to pass a value to tax_rate
# (Because all arguments get sucked into *prices)
# def calculate_wrong(*prices, tax_rate):
# pass
If you define arguments after *args, they become “Keyword-Only Arguments” and must be specified in the format argument_name=value when called.
Unpacking Lists or Tuples
If you already have data as a list or tuple and want to pass it to a function with variable-length arguments, use * at the calling side to unpack the data.
log_data = ["Error Occurred", "Rebooting", "Recovered"]
# If passed as is, the tuple element becomes "one list"
# print_server_log("App-Server", log_data)
# -> messages = (['Error Occurred', 'Rebooting', 'Recovered'],)
# Add * to unpack and pass
print_server_log("App-Server", *log_data)
# -> messages = ('Error Occurred', 'Rebooting', 'Recovered')
Summary
- Adding
*to an argument (e.g.,*args) allows you to accept an arbitrary number of positional arguments as a tuple. *argsmust be defined after normal positional arguments.- Adding
*to a list at the calling side unpacks the elements and passes them to the function.
