When to Use Arguments in Python: Absolute Beginners Tutorial

Python functions become genuinely reusable only when they accept data from the outside world. Arguments are how that data gets in. Knowing which argument type to reach for — and when — is one of the first skills that separates code that works once from code that works everywhere.

Every time you define a function in Python, you decide what information it needs to do its job. That decision shapes how flexible, readable, and reusable your code becomes. This tutorial walks through each argument type from scratch, shows you exactly when each one belongs in your code, and includes hands-on exercises to cement the patterns.

Parameters vs. Arguments: the distinction that matters

These two words get used interchangeably in conversation, but they refer to different things in the code itself. A parameter is the name you list inside the parentheses when you write a def statement. An argument is the concrete value you supply when you call the function. The parameter is the slot; the argument fills the slot.

python
# 'name' is a parameter — it exists in the function definition
def greet(name):
    print(f"Hello, {name}!")

# "Alice" is an argument — it is the value passed at call time
greet("Alice")
# Output: Hello, Alice!
Note

The Python documentation uses "parameter" for the definition side and "argument" for the call side. In practice, people often say "argument" for both. The important thing is recognizing which side of the def you are on.

code builder click a token to place it

Build a function definition that takes one parameter called city and prints it:

your code will appear here...
| greet_city print ( def city ) : return
Why: A function definition begins with def, followed by the function name, then parentheses containing the parameter, and ends with a colon. print and return are not part of the signature line — they go in the body below.

The five argument types and when to use each

Python gives you five distinct argument styles. Each solves a different problem. The table below summarizes them before each is covered in depth.

Type Syntax in definition When to use it
Positionaldef f(x, y)Required values whose order is natural and obvious
KeywordCalled as f(x=1, y=2)When you want to name arguments at the call site for clarity
Defaultdef f(x, y=10)Optional values with a sensible fallback
*argsdef f(*args)Unknown number of positional values
**kwargsdef f(**kwargs)Unknown number of named values

Positional arguments

Positional arguments are the simplest form. The caller passes values in the exact order the parameters appear in the definition. If the order is wrong, the values land in the wrong slots — Python has no way to know you made a mistake.

python
def describe_planet(name, diameter_km, moons):
    print(f"{name}: {diameter_km:,} km wide, {moons} moon(s)")

describe_planet("Mars", 6_779, 2)
# Output: Mars: 6,779 km wide, 2 moon(s)

# Wrong order — no error, but the output is nonsense
describe_planet(2, "Mars", 6_779)
# Output: 2: Mars km wide, 6779 moon(s)
Pro Tip

Use positional arguments when the order is self-evident — like coordinates (x, y) or a range (start, stop). When callers might confuse the order, keyword arguments are a safer choice.

Keyword arguments

Any function that accepts positional arguments can also be called using the parameter names explicitly. When you supply argument names at the call site, order no longer matters. This is especially useful for functions with several parameters where the correct order is not obvious.

python
def create_user(username, email, role):
    return {"username": username, "email": email, "role": role}

# Positional call — relies on caller knowing the order
create_user("jdoe", "jdoe@example.com", "admin")

# Keyword call — order does not matter, intent is explicit
create_user(email="jdoe@example.com", role="admin", username="jdoe")

Default arguments

A default argument gives a parameter a value to fall back on when the caller does not supply one. This makes certain inputs optional without requiring you to write separate versions of the function.

python
def send_message(recipient, message, priority="normal"):
    print(f"[{priority.upper()}] To {recipient}: {message}")

# Caller does not care about priority — default applies
send_message("alice", "Your report is ready")
# Output: [NORMAL] To alice: Your report is ready

# Caller overrides the default
send_message("bob", "Server is down", priority="urgent")
# Output: [URGENT] To bob: Server is down
Watch out

Never use a mutable object — a list, set, or dictionary — as a default value. Python creates the default once at function definition time, not each time the function runs. Any mutation persists across calls. Use None as the default and create the mutable object inside the function body instead.

python
# Dangerous — the list is shared across all calls
def add_item_bad(item, container=[]):
    container.append(item)
    return container

# Safe — a new list is created each time
def add_item_good(item, container=None):
    if container is None:
        container = []
    container.append(item)
    return container

*args — variable positional arguments

When you prefix a parameter with a single asterisk, Python collects all extra positional arguments into a tuple under that name. The name args is a convention; the asterisk is what matters. Use *args when you genuinely cannot predict how many values the caller will pass.

python
def total_score(*scores):
    # 'scores' is a tuple of whatever the caller passes
    return sum(scores)

print(total_score(88, 92, 76))          # 256
print(total_score(100))                  # 100
print(total_score(70, 80, 90, 85, 95))  # 420

**kwargs — variable keyword arguments

Two asterisks collect any extra named arguments into a dictionary. Each key is the argument name the caller used; each value is what was passed. **kwargs appears frequently in wrapper functions and in code that forwards options to another function.

python
def build_profile(**details):
    # 'details' is a dict of whatever named args the caller passed
    for key, value in details.items():
        print(f"  {key}: {value}")

build_profile(name="Sam", role="engineer", team="infra")
# Output:
#   name: Sam
#   role: engineer
#   team: infra
"The best interface is one where each argument name reads like documentation." — Python community convention
When to use: The value is always required and the order of parameters makes intuitive sense (e.g., move(x, y)). The caller must supply every positional argument or Python raises a TypeError.

When to avoid: Functions with many parameters that look similar — a call like create(True, False, True, 10) is unreadable. Switch to keyword arguments for clarity.
When to use: Any time naming the argument makes the call easier to read. This is a calling style, not a special definition. Any positional parameter can be supplied as a keyword argument by the caller.

Example: send("bob", message="Hello", priority="high") is clearer than relying on callers to remember the order.
When to use: When a parameter has a value that is correct for the common case. Callers who need different behavior can override it; callers who do not care skip it entirely.

Constraint: Default arguments must come after all positional (non-default) arguments in the signature. def f(x=1, y) raises a SyntaxError.
When to use: Your function needs to work on an open-ended collection of values — like a sum, a print wrapper, or a chain of transformations — and forcing the caller to bundle them into a list first would be awkward.

Inside the function: args is a plain tuple. You can loop over it with for item in args or pass it elsewhere with the unpacking operator *args.
When to use: You are building a flexible interface where callers may pass different named options — configuration builders, template renderers, or wrapper functions that forward options to an underlying library.

Inside the function: kwargs is a plain dictionary. Access values with kwargs.get("key", default) to avoid KeyError when a key might be absent.
spot the bug click the line that contains the error

One line in this function definition will raise a SyntaxError. Which line is it?

1 def register(username, role="user", email):
2 print(f"Registering {username} as {role}")
3 print(f"Email: {email}")
Fix: Default arguments must come after all non-default (positional) arguments. Move email before role="user": def register(username, email, role="user"):. Python cannot allow a default before a non-default because the call site would be ambiguous.

Argument ordering rules

Python enforces a strict ordering rule for function signatures. When you combine multiple argument types, they must appear in this sequence: positional arguments first, then default arguments, then *args, then keyword-only arguments (those defined after *args), and finally **kwargs. Any other arrangement is a SyntaxError.

python
# Valid signature combining all five argument types
def process(
    source,          # positional — required
    dest="output",   # default — optional
    *files,          # *args — zero or more extra positional values
    verbose=False,   # keyword-only (comes after *args)
    **options        # **kwargs — zero or more extra named values
):
    pass

# Valid calls
process("data.csv")
process("data.csv", "results/", "extra1.csv", verbose=True)
process("data.csv", compression="gzip", encoding="utf-8")
Note

Any parameter that appears after a *args parameter automatically becomes keyword-only. The caller must supply it by name; Python will not accept it positionally.

How to choose the right argument type for a Python function

Working through a short set of questions at design time keeps function signatures clean and avoids argument-related bugs later.

  1. Ask: is the value required every time?

    If the function cannot run without the value, make it a positional argument. No default, no flexibility — the caller must supply it or Python raises a TypeError. This is the right choice for things like a filename, a target URL, or a user ID.

  2. Ask: is there a sensible fallback?

    If many callers will want the same value but an override is occasionally needed, add a default. Common examples include a timeout, a log level, or an output format. Place default arguments after all positional ones, and never use a mutable object as the default.

  3. Ask: does the caller need to name the argument?

    If the function has several parameters that could be confused with each other, encourage keyword-style calls in your documentation. If you want to require naming at the call site, place the parameter after a bare * or after *args. Keyword-only arguments raise a TypeError if the caller omits the name.

  4. Ask: is the count unknown?

    If the number of values the caller will pass cannot be determined in advance, use *args for positional values or **kwargs for named values. Both allow zero extra arguments, so a caller who passes nothing will not trigger an error.

  5. Arrange arguments in the correct order

    Place positional arguments first, then defaults, then *args, then keyword-only arguments, and finally **kwargs. Python checks this at definition time. A violation raises a SyntaxError before any code runs.

Python Learning Summary Points

  1. A parameter is the name in the function definition; an argument is the value passed at the call site. The distinction matters when reading error messages.
  2. Positional arguments are required and order-dependent. Default arguments are optional with a fallback. Keyword style is a calling convention that works with any positional or default parameter.
  3. *args collects extra positional values into a tuple. **kwargs collects extra named values into a dictionary. Both handle an unknown number of inputs.
  4. The valid signature order is: positional, default, *args, keyword-only, **kwargs. Deviating raises a SyntaxError.
  5. Never use a mutable object as a default argument value. Use None and create the object inside the function body.

Choosing the right argument type is a design decision. Positional arguments keep required inputs obvious. Defaults reduce boilerplate for callers who share the common case. Keyword style makes long call sites readable. *args and **kwargs keep your functions open to inputs you have not anticipated. Applying each at the right time produces functions that are both flexible and easy to reason about.

check your understanding question 1 of 5

Frequently Asked Questions

A parameter is the variable name listed inside the parentheses when you define a function. An argument is the actual value you pass when you call that function. The parameter is the placeholder; the argument fills it.

Use a default argument when a value is optional and you have a sensible fallback. For example, a greeting function might default to "Hello" so callers who do not care about the greeting style do not need to supply one.

*args lets a function accept any number of positional arguments. Inside the function they are available as a tuple. Use *args when you do not know ahead of time how many values the caller will pass.

**kwargs lets a function accept any number of keyword arguments as a dictionary. The caller names each extra value, and the function receives a dict mapping those names to their values.

Yes. Positional arguments must come first, followed by keyword arguments. Swapping that order raises a SyntaxError.

A TypeError on a function call almost always means the number or names of arguments do not match what the function definition expects. Check that you are passing the right number of values and that keyword argument names are spelled correctly.

Use *args when you want the caller to supply multiple separate values without wrapping them in a list first. Use a list parameter when the data is already a list or when you want to make it obvious the caller should package values together.

The correct order is: positional arguments, then default arguments, then *args, then keyword-only arguments, then **kwargs. Placing a default argument before a positional one raises a SyntaxError.