Python Function Arguments
Python allows us to define three types of arguments for a function:
- Formal Arguments, for example
def add(a, b)
- Variable number of non-keyworded arguments using *args, for example
def add(*args)
- Variable number of keyworded arguments or named arguments using **kwargs, for example
def add(**kwargs)
.
Some important points about function arguments.
- When we define a function arguments, the order should be formal arguments followed by *args and **kwargs.
- It’s not mandatory to use the names args and kwargs, we can use other names too. However, it’s the convention to use them. It will make your code easy to read and understand.
- The args variable is of type tuple. We can pass a tuple as a function argument to map with args.
- The kwargs variable is of type dict. So we can pass a dictionary as an argument to map with kwargs.
Why do we need *args and **kwargs?
Let’s say we have a function to add two numbers:
1 2 3 4 |
def add_two_numbers(a, b): return a + b |
Now we want to extend this to add three numbers. We can’t just change this function because it might be getting used at some other places and it will be a breaking change. So, we introduce another function to add three numbers.
1 2 3 4 |
def add_three_numbers(a, b, c): return a + b + c |
You see where I am going with this, right? Since we have the option to specify a variable number of arguments for a function, we can define a generic function to add numbers.
Python *args example
Let’s define a generic function to add numbers using *args variables.
1 2 3 4 5 6 7 8 9 10 |
def add(*args): total = 0 for arg in args: total = total + arg return total print(add(1, 2)) print(add(1, 2, 3)) print(add(1, 2, 3, 4)) |
What is the type of *args and **kwargs?
1 2 3 4 5 6 |
def zap(*args, **kwargs): print(type(args)) print(type(kwargs)) zap() |
Output:
1 2 3 4 |
<class 'tuple'> <class 'dict'> |
Python **kwargs example
Let’s define a function to show the usage of **kwargs variables.
1 2 3 4 5 6 7 |
def kwargs_processor(**kwargs): for k, v in kwargs.items(): print(f'Key={k} and Value={v}') kwargs_processor(name="Pankaj", age=34) kwargs_processor(country='India', capital="New Delhi") |
Output:
1 2 3 4 5 6 |
Key=name and Value=Pankaj Key=age and Value=34 Key=country and Value=India Key=capital and Value=New Delhi |
Passing Tuple and Dictionary for *args and **kwargs mapping
Let’s see how to pass tuple values to map with args and dictionary elements to the kwargs variable.
1 2 3 4 5 6 |
t = (10, 30, 60) print(add(*t)) d = {'name': 'Pankaj', 'age': 34} kwargs_processor(**d) |
Output:
1 2 3 4 5 |
100 Key=name and Value=Pankaj Key=age and Value=34 |
Notice the use of * while using a tuple to map its values to args. Similarly, ** is used to map dict elements to the kwargs variable.
When to use *args and **kwargs
You can use them in any function where you are expecting a variable number of arguments. However, they are very useful in two specific cases – Simulating a function response, and writing a generic decorator function.
Simulating a function response with *args and **kwargs
Let’s say we have an API class defined like this:
1 2 3 4 5 6 |
class APIHelper: def call_api(self, url, input_json): # some complex logic return 'complex_response_data' |
We can create a test simulator function and map it to the API function to generate a test response.
1 2 3 4 5 6 7 |
class MyTestClass: def test_call_api(self, *args, **kwargs): return "test_response_data" # Assign API function to use our test function APIHelper.call_api = MyTestClass.test_call_api |
Let’s see what happens when we use our API functions now.
1 2 3 4 5 6 |
ah = APIHelper() print(ah.call_api()) print(ah.call_api(1, url="https://www.journaldev.com", input_json={})) print(ah.call_api(1, 2, url="https://www.journaldev.com")) |
Output:
1 2 3 4 5 |
test_response_data test_response_data test_response_data |
Decorator function for *args and **kwargs
Let’s see how we can define a decorator function to log function variables.
1 2 3 4 5 6 7 |
def log_arguments(func): def inner(*args, **kwargs): print(f'Arguments for args:{args}, kwargs:{kwargs}') return func(*args, **kwargs) return inner |
Now, we can use this decorator function with any other function to log their arguments to console.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@log_arguments def foo(x, y, z): return x + y + z sum_ints = foo(1, 2, z=5) print(sum_ints) @log_arguments def bar(x, *args, **kwargs): total = x for arg in args: total = total + arg for key, value in kwargs.items(): total = total + value return total sum_ints = bar(1, 2, 3, a=4, b=5) print(sum_ints) |
Output:
1 2 3 4 5 6 |
Arguments for args:(1, 2), kwargs:{'z': 5} 8 Arguments for args:(1, 2, 3), kwargs:{'a': 4, 'b': 5} 15 |