What is *args in Python? Definition and examples

Discover *args in Python: syntax, how it works, and practical examples for passing a variable number of positional arguments to a function.
8 min read
Believemy logo

Definition of *args in Python

In Python, *args is a syntactic convention used in the definition of a def to allow that function to accept a variable number of positional arguments. The asterisk * is the unpacking operator, and args is simply a conventional name — you could technically use any other name, like *values or *elements, but the *args convention is universally adopted by the Python community.

When you use *args in a function signature, Python automatically groups all additional positional arguments into a tuple. This means you can call your function with zero, one, ten, or even a hundred arguments without modifying its definition.

If you want to master this concept and many other language fundamentals, we recommend following our complete Python course which covers advanced function mechanisms in depth.

Good to know

*args collects positional arguments as a tuple. To collect named (keyword) arguments, use **kwargs (keyword arguments).


Basic syntax of *args

The syntax of *args is simple: just place an asterisk * before the parameter name in the function definition.

PYTHON
def my_function(*args):
    print(type(args))  # <class 'tuple'>
    for arg in args:
        print(arg)

my_function(1, 2, 3)
# 1
# 2
# 3

In this example, the three arguments 1, 2, and 3 are automatically grouped into the tuple (1, 2, 3) accessible via the variable args.


Practical examples of using *args

Sum function with a variable number of arguments

One of the most classic use cases of *args is creating a function capable of adding any number of values:

PYTHON
def total(*args):
    result = 0
    for number in args:
        result += number
    return result

print(total(1, 2))           # 3
print(total(10, 20, 30))     # 60
print(total(5))              # 5
print(total())               # 0

Notice that the function works even without arguments: in that case, args is simply an empty tuple ().


Combining regular parameters and *args

You can perfectly combine regular parameters with *args. The regular parameters are filled first, and the remaining arguments are captured by *args:

PYTHON
def greet(greeting, *args):
    for name in args:
        print(f"{greeting}, {name}!")

greet("Hello", "Alice", "Bob", "Charlie")
# Hello, Alice!
# Hello, Bob!
# Hello, Charlie!

Here, "Hello" is captured by the greeting parameter, and the three following names are grouped into args as ("Alice", "Bob", "Charlie"). Note the use of f-string to format the output.


Using *args to forward arguments to another function

A very common use of *args is argument forwarding from one function to another. This is particularly useful for creating wrapper functions or decorators:

PYTHON
def log(func, *args):
    print(f"Calling {func.__name__} with arguments: {args}")
    result = func(*args)
    print(f"Result: {result}")
    return result

def multiply(a, b):
    return a * b

log(multiply, 4, 5)
# Calling multiply with arguments: (4, 5)
# Result: 20

Observe that *args is used twice: first to collect the arguments into the tuple args, and a second time with func(*args) to unpack that tuple and pass each element as an individual argument to func.


Creating a decorator with *args and **kwargs

Decorators are one of the most powerful use cases for *args. By combining *args and **kwargs, you can create a decorator that works with any function, regardless of its signature:

PYTHON
import time

def measure_time(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} took {end - start:.4f} seconds")
        return result
    return wrapper

@measure_time
def calculate_sum(*args):
    return sum(args)

calculate_sum(1, 2, 3, 4, 5)
# calculate_sum took 0.0000 seconds


Unpacking a list or tuple with *

The * operator is not only used in function definitions. You can also use it to unpack a list or a tuple when calling a function:

PYTHON
def display_coordinates(x, y, z):
    print(f"x={x}, y={y}, z={z}")

coordinates = [10, 20, 30]
display_coordinates(*coordinates)
# x=10, y=20, z=30

# Also works with a tuple
point = (5, 15, 25)
display_coordinates(*point)
# x=5, y=15, z=25


Extended unpacking in assignments

Since Python 3, the * operator can also be used in variable assignments to capture multiple elements:

PYTHON
first, *middle, last = [1, 2, 3, 4, 5]
print(first)    # 1
print(middle)   # [2, 3, 4]
print(last)     # 5

head, *tail = "Python"
print(head)     # P
print(tail)     # ['y', 't', 'h', 'o', 'n']
Warning

Warning: in an assignment with *, the starred variable always receives a list (not a tuple as with *args).


Parameter order in a Python function

When you combine different types of parameters, the order is strict and must be respected. Here is the order expected by Python:

PositionParameter typeExample
1Regular positional parametersa, b
2*args*args
3Keyword-only parameters (after *)key
4**kwargs**kwargs

Here is an example illustrating this complete order:

PYTHON
def complete_function(a, b, *args, option=True, **kwargs):
    print(f"a = {a}")
    print(f"b = {b}")
    print(f"args = {args}")
    print(f"option = {option}")
    print(f"kwargs = {kwargs}")

complete_function(1, 2, 3, 4, 5, option=False, name="test", value=42)
# a = 1
# b = 2
# args = (3, 4, 5)
# option = False
# kwargs = {'name': 'test', 'value': 42}
Warning

Never place *args before regular positional parameters, as this would cause an error or unexpected behavior.


Differences between *args and **kwargs

It is essential to understand the distinction between *args and **kwargs, as these two mechanisms are complementary:

Characteristic*args**kwargs
CollectsPositional argumentsKeyword arguments
Resulting typetupledict
Call syntaxf(1, 2, 3)f(a=1, b=2)
Unpacking*my_tuple**my_dict

Both can be used together, as we saw in the decorator example. Together, they allow you to create extremely flexible functions capable of receiving any combination of arguments. The dict resulting from **kwargs is particularly useful for handling configuration options.


Best practices with *args

Here are the recommendations to follow for using *args effectively and maintainably:

  • Respect the naming convention: use *args and not another name, unless a more descriptive name genuinely improves readability (like *numbers for a mathematical function).
  • Document your function: when you use *args, add a clear docstring explaining what types of arguments are expected and in what quantity.
  • Validate inputs: since *args accepts everything, remember to check the type and number of received arguments if necessary.
  • Don't overuse *args: if your function always expects exactly two or three arguments, use explicit named parameters. *args should be reserved for cases where the number of arguments is truly variable.
  • Prefer type annotations: since Python 3.11+, you can annotate *args with *args: int to indicate the expected type of each argument.
PYTHON
def average(*args: float) -> float:
    """Calculate the average of a variable number of values.
    
    Args:
        *args: The numeric values for which to
               calculate the average.
    
    Returns:
        The arithmetic mean of the values.
    
    Raises:
        ValueError: If no value is provided.
    """
    if not args:
        raise ValueError("At least one value is required")
    return sum(args) / len(args)

print(average(10, 20, 30))  # 20.0

Note the use of the len function to get the number of elements in the args tuple.


Common use cases for *args

Here are the most frequent situations where *args proves indispensable:

  • Decorators: to create generic decorators that work with any function.
  • Mathematical functions: sum, product, average, minimum, maximum on a variable number of values.
  • class inheritance: to forward arguments to the parent class constructor with super().__init__(*args).
  • Wrappers and proxies: to wrap a function call while faithfully forwarding its arguments.
  • Display functions: Python's print function itself uses *args to accept a variable number of objects to display.
  • Collection concatenation: merging multiple lists, tuples, or set passed as arguments.


Common mistakes to avoid

Here are the most frequent pitfalls related to using *args:

Forgetting the asterisk when unpacking

PYTHON
def display(a, b, c):
    print(a, b, c)

values = (1, 2, 3)

# Error: passes the entire tuple as the first argument
# display(values)  # TypeError

# Correct: unpack the tuple
display(*values)  # 1 2 3


Confusing *args (tuple) with a list

PYTHON
def modify(*args):
    # args is a tuple, therefore immutable
    # args[0] = 10  # TypeError: 'tuple' does not support item assignment
    
    # Convert to list if modification is needed
    args_list = list(args)
    args_list[0] = 10
    return args_list

print(modify(1, 2, 3))  # [10, 2, 3]


Placing *args after **kwargs

PYTHON
# SYNTAX ERROR:
# def wrong(**kwargs, *args):  # SyntaxError
#     pass

# CORRECT:
def correct(*args, **kwargs):
    pass


Frequently asked questions

Question

Is the name "args" mandatory in Python?

No, the name args is not mandatory. It is the asterisk * that provides the positional argument collection behavior. You could write *numbers, *elements, or any other valid name. However, *args is a very strong convention in the Python community (defined in PEP 8), and following it makes your code immediately understandable by other developers.


Question

Can you use *args with type annotations?

Yes, since Python 3.11 (PEP 646), you can annotate *args directly. For example, def my_function(*args: int) indicates that all collected arguments should be of type int. Before Python 3.11, you could use typing.Tuple or typing.Any for more complex annotations. These annotations have no effect at runtime, but they are used by static analysis tools like mypy.


Question

What is the difference between *args in a definition and in a function call?

In a function definition (def f(*args)), the * operator collects (packs) positional arguments into a tuple. In a function call (f(*my_list)), the * operator unpacks an iterable into individual arguments. These are two inverse operations that use the same syntax.


Question

How can I learn to master *args and advanced functions in Python?

To deepen your understanding of *args, **kwargs, decorators, and all the subtleties of functions in Python, we recommend following our Python course on Believemy. You will find practical exercises, real-world projects, and comprehensive pedagogical support to master these essential Python development concepts.

Related terms

Discover our python glossary

Browse the terms and definitions most commonly used in development with Python.

Share this article

Want to help us? Share this article on your networks or even better: on your site, in an article or in your newsletter.