Skip to main content

Python *args, **kwargs, Default Values, and Keyword Arguments

Intermediate25 min7 exercises105 XP
0/7 exercises

Think about ordering coffee. You always need to pick a size — that's required. But extras like oat milk, an extra shot, or caramel drizzle? Those are optional. You only mention them when you want them.

Python function arguments work the same way. Some are required, some have sensible defaults, and some let you pass in as many extras as you want. Mastering these patterns is what separates a beginner from someone who writes truly flexible code.

In this tutorial, you'll learn every way Python lets you pass data into functions — from basic positional arguments all the way to the powerful *args and **kwargs patterns used in real-world libraries.

Positional vs Keyword Arguments

When you call a function, Python matches arguments to parameters in two ways. Positional arguments are matched by their position — first argument goes to the first parameter. Keyword arguments are matched by name.

Positional vs keyword arguments
Loading editor...

With keyword arguments, you write parameter_name=value. This makes the call self-documenting — anyone reading the code can see exactly what each value means, even without checking the function definition.

You can mix both styles in the same call, but positional arguments must come before keyword arguments.

Mixing positional and keyword
Loading editor...

How Default Values Make Functions Flexible

Default values let you define parameters that are optional. If the caller provides a value, it's used. If not, the default kicks in.

Default values in action
Loading editor...

The size parameter has no default — it's required. The milk and sugar parameters have defaults, so they're optional. This is exactly how many real Python libraries work: a few required parameters and many optional ones with sensible defaults.

The Mutable Default Trap

This is one of Python's most famous gotchas. When you use a mutable object (like a list) as a default value, that object is created once when the function is defined — not each time the function is called.

That means every call shares the same list. Changes from one call carry over to the next. Let's see it in action:

The mutable default trap
Loading editor...

Surprised? Each call is supposed to start with a fresh list, but the list keeps growing. This happens because [] is created once at function definition time and reused on every call.

The fix is a well-known Python pattern: use None as the default, and create a new list inside the function.

The fix: use None as default
Loading editor...

What Does *args Do?

Sometimes you don't know in advance how many arguments a function will receive. Maybe you want a function that adds up any amount of numbers — 2, 5, or 100.

The *args syntax collects all extra positional arguments into a tuple. You can then loop over them, index them, or do anything you'd do with a tuple.

Using *args to accept any number of arguments
Loading editor...

The name args is just a convention. You could write *numbers, *items, or *values. The magic is in the * — it tells Python to pack all remaining positional arguments into a tuple.

Inspecting *args
Loading editor...

You can combine *args with regular parameters. Regular parameters grab their values first, then *args collects everything left over.

Regular parameter + *args
Loading editor...

What Does **kwargs Do?

While *args collects extra positional arguments, **kwargs collects extra keyword arguments into a dictionary. The keys are the argument names and the values are the argument values.

Using **kwargs
Loading editor...

Like args, the name kwargs is just a convention. You could write **info, **options, or **settings. The ** is what matters — it tells Python to pack extra keyword arguments into a dictionary.

This is incredibly useful for functions that need to be flexible. You can pass any named data without changing the function definition.

Regular parameter + **kwargs
Loading editor...

Combining All Argument Types

You can use regular parameters, defaults, *args, and **kwargs in the same function. But the order matters. Python requires this specific sequence:

  • Regular positional parameters
  • Parameters with default values
  • *args
  • **kwargs
  • All argument types together
    Loading editor...
    Too flexible (hard to understand)
    def process(*args, **kwargs):
        # What does this expect?
        pass
    Clear and explicit
    def process(data, format='json',
                verbose=False):
        # Easy to understand
        pass

    Practice Exercises

    Predict the Keyword Call
    Predict Output

    What does this code print? Type the exact output.

    def power(base, exponent=2):
        print(base ** exponent)
    
    power(5)
    power(2, 10)
    power(exponent=3, base=4)
    Loading editor...
    Greeting with a Default
    Write Code

    Write a function called greet that takes a name parameter and an optional greeting parameter with a default value of 'Hello'. It should print <greeting>, <name>!. For example, greet('Alice') prints Hello, Alice! and greet('Bob', 'Hey') prints Hey, Bob!.

    Loading editor...
    Sum All Numbers
    Write Code

    Write a function called sum_all that uses *args to accept any number of numeric arguments and returns their sum. If no arguments are given, return 0.

    Loading editor...
    Fix the Mutable Default
    Fix the Bug

    This function is supposed to create a new list each time, but it has the mutable default bug. Fix it so each call starts with a fresh list.

    Expected output:

    [1]
    [2]
    [3]
    Loading editor...
    Build a Profile String
    Write Code

    Write a function called build_profile that takes a required name parameter and uses **kwargs to accept any additional info. It should print the name on the first line, then each key-value pair on subsequent lines in the format key: value (two leading spaces). Print keys in the order they are passed.

    Example: build_profile('Alice', age=25, city='NYC') should print:

    Alice
      age: 25
      city: NYC
    Loading editor...
    Flexible Calculator
    Write Code

    Write a function called calc that takes an operation string ('add', 'multiply', or 'max') and then any number of numeric arguments using *args. It should return the result:

  • 'add': sum of all numbers (0 if none)
  • 'multiply': product of all numbers (1 if none)
  • 'max': largest number (0 if none)
  • Examples: calc('add', 1, 2, 3) returns 6. calc('multiply', 2, 3, 4) returns 24.

    Loading editor...
    Refactor to Use **kwargs
    Refactor

    Refactor the function below to use **kwargs instead of individual parameters. The function should accept name as a required parameter and everything else through **kwargs. Print the name, then print each detail on its own line in key: value format. The output must match exactly.

    Original:

    def show_student(name, grade, school, gpa):
        print(name)
        print('grade: ' + str(grade))
        print('school: ' + school)
        print('gpa: ' + str(gpa))

    Expected output for show_student('Alice', grade=10, school='Lincoln', gpa=3.8):

    Alice
    grade: 10
    school: Lincoln
    gpa: 3.8
    Loading editor...