Table of Contents

What Will You Learn
In this tutorial, you'll learn how Python's *args and **kwargs enable you to write functions that accept a dynamic number of arguments. You'll explore how to use them effectively, the difference between positional and keyword arguments, and real-world use cases. By the end, you'll write more flexible, reusable, and professional-looking Python code.


As a beginner in Python, you’ll quickly notice that functions vary in how they receive input. Sometimes, the number of arguments is unknown or flexible — and that’s exactly where *args becomes essential. Understanding *args equips you with the ability to write functions that accept any number of positional arguments. This makes your code more dynamic, reusable, and scalable.

You’ll find *args used in many standard libraries, decorators, and frameworks. It allows functions to remain clean while still being adaptable to future changes. Whether you're working with APIs, data pipelines, or utility functions — mastering *args is a foundational skill that will make your code more professional and future-proof. Learning it early builds your confidence in handling real-world coding challenges.

What Does *args Mean in Python?

In Python, *args allows you to pass a variable number of positional arguments to a function. The asterisk (*) collects all additional unnamed arguments into a single tuple named args. This enables your function to accept zero, one, or many arguments without modifying the function signature. It’s especially useful when you don’t know how many inputs a user or external module might provide.

By convention, the name args is commonly used, but any valid variable name will work after the asterisk. You can loop over args inside the function to process each value. It is important to note that *args must be placed after all regular positional parameters. If used with **kwargs (keyword arguments), *args should appear first in the function definition. While it may look simple, *args is a powerful pattern that helps you write more flexible and generalized code.

How to Split *args in Python?

You can "split" *args by accessing individual elements of the tuple inside the function. Since *args collects all extra positional arguments into a tuple, you can index it, slice it, or loop through it like any other sequence. This gives you full control over the contents and lets you process arguments selectively. It's especially helpful when the first few arguments have specific meanings, and the rest can be grouped together.


    def process_data(*args):
        first = args[0]
        others = args[1:]
        print("First:", first)
        print("Others:", others)

    process_data(10, 20, 30, 40)
    # Output: First: 10 / Others: (20, 30, 40)

How to Pass *args in Python?

To pass *args to a function, prepend the argument list with an asterisk when calling the function. This unpacks the values and passes them as separate positional arguments. It’s useful when you already have a tuple or list of values and want to pass them to a function that accepts multiple parameters. This approach keeps the code clean and dynamic.


    def greet(name, age):
        print(f"{name} is {age} years old.")

    data = ("Alice", 30)
    greet(*data)
    # Output: Alice is 30 years old.

How to Unpack *args in Python?

Unpacking *args means extracting individual values from a tuple of arguments passed to a function. You can use the asterisk * during assignment or within a function signature. This technique is helpful when you want to distribute values into multiple variables or when forwarding arguments to another function. Python handles this unpacking intuitively.


    # Unpack into variables
    def demo_unpack(*args):
        a, b, *rest = args
        print("a:", a)
        print("b:", b)
        print("rest:", rest)

    demo_unpack(1, 2, 3, 4, 5)
    # Output: a: 1 / b: 2 / rest: [3, 4, 5]

How to Use *args in Python

Using *args allows your function to accept any number of positional arguments as a tuple. You define it in the function signature by adding an asterisk before a parameter name, commonly *args. Inside the function, you can loop over args, access it with indexing, or apply operations to all arguments. This makes the function more flexible and suitable for unknown input lengths. It’s particularly useful in decorators, helper functions, and mathematical utilities.

Keep in mind that *args must appear after standard positional parameters and before **kwargs if both are used.

What Are Some Practical Examples of Using *args in Python?

*args is commonly used in scenarios where input length may vary or when you want to keep a function flexible and concise. It is ideal for mathematical operations, formatting utilities, or when building wrapper functions. You will often see it in frameworks, decorators, and utility toolkits. The ability to accept multiple arguments without defining each one makes code reusable and cleaner. Below are real-world uses for *args in Python:

  • Summing numbers: Pass any number of values and return the total.
  • Joining strings: Concatenate multiple string values into one output.
  • Flexible logging: Print logs with any number of values using a single log function.
  • Mathematical operations: Write calculators that accept a dynamic number of inputs.
  • Command-line utilities: Handle varying user inputs passed to a function.
  • Frameworks and decorators: Pass unknown arguments to wrapped or inner functions.
  • UI functions: Dynamically pass layout or styling arguments to GUI elements.

What Are the Differences Between *args and **kwargs in Python?

*args and **kwargs are used to pass a variable number of arguments to a function, but they serve different purposes. *args collects extra positional arguments as a tuple, while **kwargs collects extra keyword arguments as a dictionary. This means *args is used when you don’t know how many positional arguments you’ll receive, and **kwargs is used when you don’t know how many named arguments will be passed. They can be used together in the same function, but *args must come first in the parameter list. You can also combine them with standard parameters for flexible function definitions. They are useful for decorators, API wrappers, GUI development, and extensible utility functions. Using them correctly makes your code clean, reusable, and future-proof.

Feature *args **kwargs
Type collected Tuple of values Dictionary of key-value pairs
Used for Positional arguments Keyword arguments
Unpacking syntax * **
Typical usage Math operations, utilities Configuration, styling, options
Data structure Tuple Dict
Call style func(1, 2, 3) func(a=1, b=2)
Order in definition Must appear before **kwargs Must appear after *args

Common Beginner Mistakes

Passing Keyword Arguments to *args

A common mistake is trying to pass keyword arguments to a function that only accepts *args. Since *args only collects positional values, any keyword-based input will result in a TypeError. To fix this, either add **kwargs to the function signature or avoid passing named arguments. Always match the function’s parameter style when calling it.


    # Incorrect
    def show_values(*args):
        print(args)

    show_values(a=1, b=2)  # TypeError

    # Correct
    def show_values(**kwargs):
        print(kwargs)

Forgetting to Unpack *args When Passing

Beginners often forget to unpack a list or tuple using * when passing it to a function that expects separate arguments. As a result, the function receives the entire list as a single item instead of individual elements. Use * before the variable to expand it during the function call. This ensures each item is passed as a separate argument.


    # Incorrect
    def add(a, b):
        return a + b

    nums = [2, 3]
    add(nums)  # TypeError

    # Correct
    add(*nums)  # Output: 5

Using *args Without Any Parameters

Another mistake is defining *args in a function and not using it. While syntactically valid, this adds confusion and suggests that the function accepts more arguments than it actually uses. If you don’t intend to process extra arguments, remove *args from the definition to make the code clearer and easier to maintain.


    # Unnecessary
    def greet(name, *args):
        print(f"Hello, {name}")  # args not used

    # Better
    def greet(name):
        print(f"Hello, {name}")

Incorrect Parameter Order

When using *args along with other parameters, putting them in the wrong order causes syntax errors. In Python, *args must come after regular positional arguments and before **kwargs. Always follow the correct sequence: regular parameters → *args**kwargs. This ensures your function is defined and called properly.


    # Incorrect
    def func(*args, x, y):  # SyntaxError

    # Correct
    def func(x, y, *args):
        pass

Modifying *args Directly

*args is a tuple, which is immutable. Trying to append or modify its contents directly will raise an error. If you need to manipulate the arguments, first convert args to a list. This allows you to add, remove, or update items without errors.


    # Incorrect
    def update_args(*args):
        args.append(5)  # AttributeError

    # Correct
    def update_args(*args):
        args_list = list(args)
        args_list.append(5)

Frequently Asked Questions

What are *args and **kwargs in Python functions?

In Python, *args and **kwargs allow you to define functions that accept a flexible number of arguments. *args is used to collect extra positional arguments into a tuple. You can loop through args or access its items using indexes. On the other hand, **kwargs collects keyword arguments into a dictionary. This is useful when you want to allow optional, named parameters in your function.

For example:


      def example(*args, **kwargs):
          print(args)
          print(kwargs)

      example(1, 2, name="Alice", active=True)
      # Output: (1, 2)
      # {'name': 'Alice', 'active': True} 

These features are commonly used in APIs, decorators, wrappers, and dynamic function calls.

Can I use *args and normal parameters together?

Yes, you can use *args alongside regular parameters in the same function. Just make sure to place regular parameters before *args in the function definition. The standard order is: positional parameters → *args → default parameters → **kwargs. This structure allows you to handle both required and variable-length inputs in a clean and predictable way.

For example:


      def report(title, *items):
          print("Report:", title)
          for item in items:
              print("-", item)

      report("To-Do List", "Task 1", "Task 2", "Task 3")

In this setup, title is required, and any number of additional tasks can follow.

How do I forward *args to another function?

You can forward *args from one function to another by using the unpacking operator (*) when calling the next function. This is particularly useful when writing wrapper functions, decorators, or middleware that extend existing functionality without modifying the inner function’s signature. The same pattern applies to **kwargs using double asterisks (**).

Example:


      def log(*args):
          print("LOG:", *args)

      def wrapper(*args):
          log(*args)

      wrapper("File saved", 200, "OK")
      # Output: LOG: File saved 200 OK
    

This technique helps you create flexible and reusable function structures.

What happens if I don’t use the unpacking operator with *args?

If you pass a list or tuple to a function expecting multiple arguments and forget to unpack it using the * operator, Python treats the list as a single positional argument. This often results in a TypeError if the function was expecting individual arguments. Always use * to unpack the elements when passing them into a function expecting multiple inputs.

For example:


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

      pair = (4, 5)

      # Incorrect:
      # multiply(pair) → TypeError

      # Correct:
      multiply(*pair)  # Output: 20
    

This ensures each element is passed to the correct parameter position.

Are there limits to how many arguments I can pass using *args?

Technically, there is no predefined limit in Python on how many arguments you can pass using *args. However, practical limitations exist depending on memory and stack size. Passing hundreds or thousands of arguments is rarely useful and can lead to code that is difficult to maintain or debug. In such cases, it’s better to pass a list or other data structure instead of relying on *args.

Keep in mind that functions with flexible signatures are powerful but should still follow clean design principles. Use *args when you truly need flexibility — not as a way to avoid thinking about your inputs.