Python Arguments
Providing Input to Functions
In the previous article, we learned that functions are reusable blocks of code. To make functions flexible and perform actions on different data, we can pass information into them. These pieces of information are handled by arguments and parameters.
Understanding how to define and use different types of arguments is essential for writing functions that are powerful, flexible, and easy to use correctly.
Parameters vs. Arguments
It's common to hear the terms "parameters" and "arguments" used interchangeably, but in Python, they have distinct meanings:
- Parameters: These are the names listed in the function definition that specify what kinds of information the function can accept. They are like placeholders for the values the function needs.
def greet(name): # 'name' is a parameter print(f"Hello, {name}!")
- Arguments: These are the actual values you pass to the function when you call it. The arguments are assigned to the corresponding parameters.
greet("Alice") # "Alice" is an argument
Think of parameters as the variables declared in the function's blueprint, and arguments as the specific values you supply when you build or use that blueprint.
Positional Arguments
Positional arguments are the simplest type. When you call a function, you provide values in the same order as the parameters are defined in the function signature. Python matches the arguments to the parameters based solely on their position.
def describe_pet(animal_type, pet_name):
print(f"I have a {animal_type}.")
print(f"My {animal_type}'s name is {pet_name}.")
# The order matters! 'dog' goes to animal_type, 'Buddy' goes to pet_name
describe_pet('dog', 'Buddy')
Output:
I have a dog.
My dog's name is Buddy.
If you change the order, the meaning changes (or you might get an error if types are incompatible):
describe_pet('Buddy', 'dog')
Output:
I have a Buddy.
My Buddy's name is dog.
Keyword Arguments
With keyword arguments, you specify which parameter each argument should be assigned to by using the parameter name followed by an equals sign (=
).
The main advantage of keyword arguments is that the order does not matter, and the code becomes more readable because the purpose of each argument is clear.
def describe_pet(animal_type, pet_name):
print(f"I have a {animal_type}.")
print(f"My {animal_type}'s name is {pet_name}.")
# Using keyword arguments - order doesn't affect assignment
describe_pet(pet_name='Hamster', animal_type='rodent')
Output:
I have a rodent.
My rodent's name is Hamster.
You can mix positional and keyword arguments in a single call, but all positional arguments must come before any keyword arguments.
describe_pet('cat', pet_name='Whiskers') # Positional first, then keyword - OK
# describe_pet(pet_name='Whiskers', 'cat') # Keyword first, then positional - ERROR!
# SyntaxError: positional argument follows keyword argument
Default Argument Values
You can give a parameter a default value in the function definition. If the caller doesn't provide an argument for that parameter, the default value is used. If an argument is provided, the default is ignored.
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
# Using the default greeting
greet("Alice")
# Providing a different greeting
greet("Bob", "Hi")
Output:
Hello, Alice!
Hi, Bob!
Parameters with default values must come after parameters without default values in the function definition.
# def invalid_func(greeting="Hello", name): # ERROR! Non-default parameter follows default parameter
# print(f"{greeting}, {name}!")
(Brief note: Be cautious using mutable objects like lists or dictionaries as default values, as they can lead to unexpected behavior. For beginner purposes, usually stick to immutable defaults like strings, numbers, or None
.)
Variable-Length Positional Arguments (*args
)
Sometimes, you want a function to accept any number of positional arguments. Python allows this using a parameter prefixed with a single asterisk (*
). By convention, this parameter is often named *args
.
When the function is called, all extra positional arguments that are not assigned to named parameters are collected into a tuple and assigned to the *args
parameter.
def sum_all_numbers(*args):
print(f"args is: {args}") # args is a tuple
return sum(args)
print(sum_all_numbers(1, 2, 3)) # Output: args is: (1, 2, 3)\n6
print(sum_all_numbers(10, 20, 30, 40)) # Output: args is: (10, 20, 30, 40)\n100
print(sum_all_numbers()) # Output: args is: ()\n0
*args
must be placed after any standard positional parameters.
Variable-Length Keyword Arguments (**kwargs
)
Similarly, you might want a function to accept any number of keyword arguments. You can do this using a parameter prefixed with two asterisks (**
). By convention, this parameter is often named **kwargs
.
When the function is called, all extra keyword arguments that are not assigned to named parameters are collected into a dictionary and assigned to the **kwargs
parameter.
def print_user_info(**kwargs):
print(f"kwargs is: {kwargs}") # kwargs is a dictionary
for key, value in kwargs.items():
print(f"{key}: {value}")
print_user_info(name="Alice", age=30, city="New York")
# Output: kwargs is: {'name': 'Alice', 'age': 30, 'city': 'New York'}\nname: Alice\nage: 30\ncity: New York
print_user_info(product="Laptop", price=1200) # Different keywords
# Output: kwargs is: {'product': 'Laptop', 'price': 1200}\nproduct: Laptop\nprice: 1200
print_user_info() # No keyword arguments
# Output: kwargs is: {}
**kwargs
must be the last parameter in the function definition.
Forcing Argument Passing Style (/
and *
)
Python offers special syntax to control how callers must pass arguments for certain parameters.
Positional-Only Parameters (/
)
Parameters listed before a single forward slash (/
) can only be passed using their position (as positional arguments). You cannot use their name as a keyword argument.
def pos_only_example(a, b, /, c, d):
# a and b are positional-only
# c and d can be positional or keyword
print(f"a: {a}, b: {b}, c: {c}, d: {d}")
pos_only_example(1, 2, 3, 4) # All positional - OK
pos_only_example(1, 2, c=3, d=4) # a, b positional; c, d keyword - OK
pos_only_example(1, 2, 3, d=4) # a, b, c positional; d keyword - OK
# pos_only_example(a=1, b=2, 3, 4) # ERROR! Cannot use keyword for positional-only
# TypeError: pos_only_example() got some positional-only arguments passed as keyword arguments: 'a, b'
# pos_only_example(1, b=2, 3, 4) # ERROR! Cannot use keyword for positional-only
# TypeError: pos_only_example() got some positional-only arguments passed as keyword arguments: 'b'
Positional-only parameters are sometimes used in built-in functions for clarity or to prevent breaking code if parameter names change later.
Keyword-Only Parameters (*
)
Parameters listed after a single asterisk (*
) (or after *args
if used) can only be passed using their keyword name. You cannot use their position to pass an argument.
def kw_only_example(a, b, *, c, d):
# a and b can be positional or keyword
# c and d are keyword-only
print(f"a: {a}, b: {b}, c: {c}, d: {d}")
kw_only_example(1, 2, c=3, d=4) # a, b positional; c, d keyword - OK
kw_only_example(a=1, b=2, c=3, d=4) # a, b keyword; c, d keyword - OK
kw_only_example(1, b=2, c=3, d=4) # a positional, b keyword; c, d keyword - OK
# kw_only_example(1, 2, 3, 4) # ERROR! Cannot use positional for keyword-only
# TypeError: kw_only_example() takes 2 positional arguments but 4 were given
# kw_only_example(1, 2, 3, d=4) # ERROR! Cannot use positional for keyword-only
# TypeError: kw_only_example() takes 2 positional arguments but 3 positional arguments (and 1 keyword-only argument) were given
Keyword-only arguments are useful for making function calls clearer (requiring the user to name certain arguments) and for adding new parameters to functions without breaking existing code that calls the function positionally.
Order of Parameters in Function Definition
When defining a function that uses multiple types of parameters, they must appear in a specific order:
- Positional-Only Parameters: Parameters before
/
. - Positional-or-Keyword Parameters: Parameters after
/
(if used) and before*
or*args
. *args
: The single asterisk parameter for collecting extra positional arguments.- Keyword-Only Parameters: Parameters after
*
(if used) or after*args
. **kwargs
: The double asterisk parameter for collecting extra keyword arguments.
Here's a function signature showing all possible types in the correct order:
def complex_example(pos1, pos2, /, pos_or_kw, *args, kw_only1, kw_only2="default", **kwargs):
print(f"pos1: {pos1}")
print(f"pos2: {pos2}")
print(f"pos_or_kw: {pos_or_kw}")
print(f"args: {args}")
print(f"kw_only1: {kw_only1}")
print(f"kw_only2: {kw_only2}")
print(f"kwargs: {kwargs}")
# Example call using different argument types
complex_example(1, 2, 3, 4, 5, kw_only1=6, extra1=7, extra2=8)
# Output:
# pos1: 1
# pos2: 2
# pos_or_kw: 3
# args: (4, 5)
# kw_only1: 6
# kw_only2: default
# kwargs: {'extra1': 7, 'extra2': 8}
Argument Unpacking (*
and **
for Calling)
You've seen *
and **
used in function definitions. They have a different meaning when used in a function call.
-
Using
*
before an iterable (like a list or tuple) in a function call unpacks its elements and passes them as positional arguments.def add(a, b, c): return a + b + c numbers = [1, 2, 3] print(add(*numbers)) # Unpacks the list [1, 2, 3] into arguments 1, 2, 3 # Output: 6 # Can also be used with ranges, tuples, strings, etc. print(add(*(range(3, 6)))) # Unpacks the range object # Output: 12 (3 + 4 + 5)
-
Using
**
before a dictionary in a function call unpacks its key-value pairs and passes them as keyword arguments.def greet(**kwargs): if "name" in kwargs: print(f"Hello, {kwargs['name']}!") if "message" in kwargs: print(kwargs['message']) info = {"name": "Alice", "message": "Nice to see you!"} greet(**info) # Unpacks the dictionary into keyword arguments name='Alice', message='Nice to see you!' # Output: Hello, Alice!\nNice to see you!
Argument unpacking is a powerful way to pass data from collections to functions.
Summary
- Parameters are in the definition; arguments are the values passed during the call.
- Positional arguments are matched by order.
- Keyword arguments are matched by name (
name=value
); order doesn't strictly matter. - Default values provide fallbacks if an argument isn't supplied.
*args
collects extra positional arguments into a tuple.**kwargs
collects extra keyword arguments into a dictionary.- Positional-only parameters (before
/
) must be passed positionally. - Keyword-only parameters (after
*
) must be passed by keyword name. - Parameters in definition follow a strict order: positional-only, positional-or-keyword,
*args
, keyword-only,**kwargs
. *iterable
and**dictionary
can be used in function calls to unpack arguments.
Understanding these different ways to handle function arguments gives you great flexibility and control when designing and using functions in Python.