Published 2022-06-09 20:45:16
Learn *args and **kwargs in Python
Learn *args and **kwargs in Python
Introduction
In this article, we are discussing args
and kwargs
that you have probably seen in various Python codes.
Normally, we pass arguments to functions and classes and we do something with the data we sent in.
Beginners usually learn how to define positional arguments that can be called by their position in the function call.
Here is a very silly function that returns a number of lemons based on the integer we sent in.
def give_me_lemons(num): # positional argument
assert num > 0, 'Can\'t give you any lemons'
lemon_word = 'lemon' if get_sum == 1 else 'lemons'
return f"I\'m giving you {get_sum} {lemon_word}"
print(give_me_lemons(2))
Output
I'm giving you 2 lemons
If you add 0 or a negative number, you would get the following:
print(give_me_lemons(0))
Output:
AssertionError: Can't give you any lemons
What if we want to add any number of arguments? That wouldn't work in our current implementation, simply because our give_me_lemons()
function requires one argument only (positional argument):
print(give_me_lemons(3, 5))
Output:
TypeError: give_me_lemons() takes 1 positional argument but 2 were given
How can we provide any number of arguments? We will solve it with an unpacking operator (*)
Any number of arguments - *args
The use case now is that we want to pass one or multiple numbers to our silly function and it will calculate the sum of lemons it wants to give us.
Be aware that the word *args is not a keyword, you can give it any name. I have deliberately used nums
in the following example.
def give_me_lemons(*nums:int): # providing *args, which is not a keyword, you can use whatever name. (*) is an unpacking operator
get_sum = sum(nums) #sum the provided numbers
assert get_sum > 0, 'Can\'t give you any lemons' # checking so no zero or negative number is provided, otherwise no lemons
lemon_word = 'lemon' if get_sum == 1 else 'lemons' # just checking singular or plural
return f"I\'m giving you {get_sum} {lemon_word}" # return lemon(s)
print(give_me_lemons(3, 5, 8))
Output:
I'm giving you 16 lemons
❗ (*) is an unpacking operator, it unpacks any number of arguments.
We will use add print(type(nums)
to check what type of object nums
is.
def give_me_lemons(*nums:int): # providing *args, which is not a keyword, you can use whatever name. (*) is an unpacking operator
print(type(nums))
get_sum = sum(nums) #sum the provided numbers
assert get_sum > 0, 'Can\'t give you any lemons' # checking so no zero or negative number is provided, otherwise no lemons
lemon_word = 'lemon' if get_sum == 1 else 'lemons' # just checking singular or plural
return f"I\'m giving you {get_sum} {lemon_word}" # return lemon(s)
print(give_me_lemons(3, 5))
Output:
<class 'tuple'>
I'm giving you 8 lemons
The nums
is a tuple which means it's an immutable object. We cannot change the value in nums
object:
def give_me_lemons(*nums:int): # providing *args, which is not a keyword, you can use whatever name. (*) is an unpacking operator
print(type(nums))
nums[0] = 4
get_sum = sum(nums) #sum the provided numbers
assert get_sum > 0, 'Can\'t give you any lemons' # checking so no zero or negative number is provided, otherwise no lemons
lemon_word = 'lemon' if get_sum == 1 else 'lemons' # just checking singular or plural
return f"I\'m giving you {get_sum} {lemon_word}" # return lemon(s)
print(give_me_lemons(3, 5))
Output:
TypeError: 'tuple' object does not support item assignment
❗Once a tuple is created, you cannot change its values.
Now were have learned what *args is, but what is **kwargs
? We will cover it in the next section.
Dictionary - **kwargs
Even in this case, "kwargs
" is not a keyword, you can give it any name.
We will go straight into finding out what kind of object **kwargs
is by modifying our function a little bit.
def give_me_lemons(**kwargs): # providing **kwargs, which is not a keyword, you can use whatever name. (**) is an unpacking operator
print(type(kwargs))
return kwargs.items()
print(give_me_lemons(bucket_1=13, bucket_2=24, bucket_3=10)) # we need to provide key-value pair
Output:
<class 'dict'>
dict_items([('bucket_1', 13), ('bucket_2', 24), ('bucket_3', 10)])
As you have noticed, the unpacking operator (**
) is used for dictionary objects. Since it expects a dictionary, we need to pass key-value pairs.
If we want to sum the number of lemons in each bucket, we'll need to use kwargs.values()
method to get the actual amount of lemons in each bucket.
def give_me_lemons(**kwargs): # providing **kwargs, which is not a keyword, you can use whatever name. (**) is an unpacking operator
get_sum = sum(kwargs.values()) #sum the provided numbers
assert get_sum > 0, 'Can\'t give you any lemons' # checking so no zero or negative number is provided, otherwise no lemons
lemon_word = 'lemon' if get_sum == 1 else 'lemons' # just checking singular or plural
return f"I\'m giving you {get_sum} {lemon_word}" # return lemon(s)
print(give_me_lemons(bucket_1=13, bucket_2=24, bucket_3=10)) # we need to provide key-value pair
Output:
I'm giving you 47 lemons
❗ Dictionaries in Python have the following built-in methods to find out items, keys, and values:
- x.items()
- x.keys()
- x.values()
Ordering Matters
In some cases, you need to have several types of arguments, both positional and changeable. When writing those functions, you need to consider the order of how you specify function parameters.
def test_me(price:int, tax:int, *args, **kwargs): # you need to have it in this order *args, **kwargs
unpack_args = args
unpack_kwargs = kwargs
sum_lemons = sum(unpack_kwargs.values())
return f"Price: ${price}, Tax: ${tax}, Orders: {unpack_args}, Items: {unpack_kwargs} Sum: {sum_lemons}"
print(test_me(2, 20, 2, 4, bucket_1=13, bucket_2=24))
Output:
Price: $2, Tax: $20, Orders: (2, 4), Items: {'bucket_1': 13, 'bucket_2': 24} Sum: 37
The following would throw an error:
def test_me(price:int, tax:int, **kwargs, *args): # you need to have it in this order *args, **kwargs
unpack_args = args
unpack_kwargs = kwargs
sum_lemons = sum(unpack_kwargs.values())
return f"Price: ${price}, Tax: ${tax}, Orders: {unpack_args}, Items: {unpack_kwargs} Sum: {sum_lemons}"
print(test_me(2, 20, 2, 4, bucket_1=13, bucket_2=24))
Output:
def test_me(price:int, tax:int, **kwargs, *args): # you need to have it in this order *args, **kwargs
^
SyntaxError: invalid syntax
❗You need to keep to following ordering rules:
- Positional arguments
*args
arguments**kwargs
arguments
Summary
We have learned the following:
- positional arguments
- *args: unpacking tuple items
- **kwargs unpacking dict items, using methods as x.items(), x.keys(), x.values()
- Argument order matters when we define the parameters of a function
About the Author
Aleksandro Matejic, Cloud Architect, began working in IT Industry over 20y ago as a technical consultant at Lindahl Rothoff in southern Sweden. Since then, he has worked in various companies and industries like Atea, Baxter, and IKEA having various architect roles. In his spare time, Aleksandro is developing and running devoriales.com, a blog and learning platform launched in 2022. In addition, he likes to read and write technical articles about software development and DevOps methods and tools. You can contact Aleksandro by paying a visit to his LinkedIn Profile.