Historical Context & Motivation
When programming languages were first created in the 1950s and 1960s, functions could only receive data in one rigid way: you had to pass values in the exact order the function expected. If a function needed a name and then an age, you could never swap them. This worked for small programs, but as software grew more complex, developers wanted more flexibility. The idea of keyword arguments — where you explicitly name each piece of data you're passing — emerged as a powerful solution. Python, designed by Guido van Rossum, embraced both styles from its earliest versions, giving programmers the freedom to choose whichever approach makes their code clearest.
So the core question this lesson addresses is: when you call a function, how do you decide whether to pass arguments by position or by keyword name? And what are the trade-offs of each approach? Let's find out.
Core Principles & Definitions
Before we dive into examples, let's define the key terms. A parameter is the variable name listed in a function's definition — it's like a placeholder waiting for a value. An argument is the actual value you pass to the function when you call it. Think of a parameter as a labeled empty box, and an argument as the item you place inside that box.
Positional Arguments
Keyword Arguments
Default Values
Mixing Styles
Visual Explanation
The diagram below illustrates how Python matches arguments to parameters in both positional and keyword styles. Notice how positional arguments rely on order, while keyword arguments use explicit naming to find their target parameter.
name, the second to greeting, and the third to punctuation. In the bottom section, keyword arguments are matched by name, so they can appear in any order.As you can see, the positional approach is concise — you just list the values. But if someone reads the call greet("Alice", "Hello", "!") without seeing the function definition, it may not be obvious which string is the name and which is the greeting. The keyword approach is a bit longer to type, but it's self-documenting — anyone reading the code can immediately understand what each argument represents.
How Python Resolves Arguments
When Python encounters a function call, it follows a specific algorithm to assign each argument to the correct parameter. Understanding this process helps you avoid common errors like TypeError and SyntaxError. Here is the step-by-step mechanism Python uses internally.
The Argument Resolution Algorithm
- Step 1: Python scans all positional arguments in the call from left to right and assigns them to parameters in the same left-to-right order.
- Step 2: Python then processes keyword arguments. For each one, it looks up the parameter with the matching name and assigns the value.
- Step 3: If any parameter still has no value and has a default defined, Python fills it with that default.
- Step 4: If any parameter still has no value and no default, Python raises a TypeError saying the argument is missing.
The Golden Rule
greet(name="Alice", "Hello", "!") causes a SyntaxError because Python sees keyword arguments followed by positional ones.Default Parameter Values
When you define a function, you can assign default values to parameters. For example, def greet(name, greeting="Hi", punctuation="."): means you only need to provide name — the other two have sensible fallbacks. Parameters with defaults must appear after parameters without defaults in the function definition. This rule ensures Python can always match positional arguments unambiguously.
Common Patterns & Classification
In real-world Python code, you'll encounter several common patterns for how positional and keyword arguments are combined. The diagram below classifies the four main calling patterns you'll use, from simplest to most advanced.
| Pattern | Example Call | Best For |
|---|---|---|
| All positional | print("hello") | Simple, well-known functions |
| All keyword | dict(name="Jo", age=16) | Clarity when argument purpose isn't obvious |
| Mixed | sorted(data, reverse=True) | Required data + optional config |
| Defaults only | open("file.txt") | Functions with many rarely-changed options |
Worked Example
Let's walk through a complete example of writing a function and calling it with different argument styles. We'll build a function that calculates a student's weighted grade.
weighted_grade that takes a student's homework score, test score, and optional weights. The weights have default values that add up to 1.0 (100%).def weighted_grade(homework, test, hw_weight=0.4, test_weight=0.6): return homework * hw_weight + test * test_weightweighted_grade(85, 92, 0.3, 0.7) → result is 85 × 0.3 + 92 × 0.7 = 25.5 + 64.4 = 89.9weighted_grade(homework=85, test=92, test_weight=0.7, hw_weight=0.3) → same result: 89.9weighted_grade(85, 92) → 85 × 0.4 + 92 × 0.6 = 34.0 + 55.2 = 89.2weighted_grade(85, 92, test_weight=0.7) → but wait — this causes a problem! hw_weight still defaults to 0.4, and 0.4 + 0.7 = 1.1. The function runs but gives a misleading grade. This shows why you should validate your inputs when designing functions with defaults.Strengths & Limitations of Each Style
Neither positional nor keyword arguments are universally "better." Each has strengths that shine in different situations. The table below compares them across the dimensions that matter most when writing clean, maintainable Python code.
| Dimension | Positional Arguments | Keyword Arguments |
|---|---|---|
| Readability | Good for 1−2 obvious args; unclear with 3+ | Excellent — each argument is self-labeled |
| Brevity | Shorter to type, less visual clutter | Longer, can feel verbose for simple calls |
| Order flexibility | None — order must match exactly | Full — any order works |
| Error risk | Easy to swap similar-type args accidentally | Typos in keyword names cause errors (but caught early) |
| With defaults | Must provide all preceding args to reach later ones | Can skip any defaulted parameter by name |
len(my_list)). Switch to keyword arguments whenever the call would be confusing without labels, especially for boolean flags like verbose=True or when a function has three or more parameters.Connection to Advanced Concepts
Once you're comfortable with basic positional and keyword arguments, Python offers even more powerful tools for handling function parameters. These advanced features build directly on the concepts you've learned in this lesson.
| This Lesson | Advanced Feature | What It Does |
|---|---|---|
| Positional arguments | *args | Collects any number of positional arguments into a tuple |
| Keyword arguments | **kwargs | Collects any number of keyword arguments into a dictionary |
| Parameter order rules | Positional-only ( / ) and keyword-only ( * ) | Force callers to use only positional or only keyword for specific params |
| Default values | Mutable default trap | Using lists/dicts as defaults is dangerous — they're shared across calls |
*args and **kwargs, which let you write functions that accept a variable number of arguments. These are the foundation for many Python libraries and frameworks. Mastering positional and keyword arguments now will make learning *args and **kwargs feel like a natural next step.Practice Problems
greet("Alice", "Hello"): greet(greeting="Hello", name="Alice")def power(base, exponent=2): that returns base ** exponent, what does each of the following calls return? (a) power(3) (b) power(3, 4) (c) power(exponent=3, base=2)def describe_pet(name, animal="dog", trick="sit"): which returns f"{name} is a {animal} that can {trick}." Write three different calls that all produce the output: "Whiskers is a cat that can sit." Use (a) all keyword arguments, (b) mixed positional and keyword, and (c) a call that relies on the default for trick.calculate_tip that takes bill_amount (required), tip_percent (default 18), and split (default 1). It should return the total per person (bill + tip divided by split). Then show how to call it for a $60 bill with 20% tip split 3 ways using mixed positional and keyword arguments.def register(name, grade=9, sport="track"): ... and then calls register("Maya", sport="soccer"). They expect grade to remain the default (9). Will this work correctly, or will Python raise an error? Explain the argument resolution process step by step. Then propose a scenario where using all positional arguments for this function could lead to a subtle, hard-to-catch bug.Lesson Summary
Python functions accept data through positional arguments, which are matched by order, and keyword arguments, which are matched by name. Parameters are the placeholders in the function definition, while arguments are the actual values passed during a call. You can mix both styles in a single call, but positional arguments must always come before keyword arguments.
Use positional arguments for simple, obvious calls with few parameters. Switch to keyword arguments when a function has many parameters, uses default values you want to selectively override, or when readability matters more than brevity. This foundation prepares you for advanced features like *args and **kwargs, which extend positional and keyword arguments to accept variable numbers of inputs.