** or arguments unpacking in Python

We all know *args and **kwargs in function parameters. It says that args are positional arguments however many you want to provide. And kwargs are keyword arguments, however many as well. We aren’t limited to name them args and kwargs, it’s just everyone do it and it’s convenient to name them this way.

def foo(a, b, *args, **kwargs):
print([arg for arg in args])
print([f'{k}: {v}' for k, v in kwargs.items()])
foo(1, 2, 3, 4, f=5, g=6)
[3, 4]
['f: 5', 'g: 6']

1 and 2 are our required arguments. 3 and 4 would be our args. f=5 and g=6 are kwargs.

But the cool thing I started to implement not so long ago is arguments unpacking. Basically, you tell your function to get a list or dictionary of arguments and use values (or keys and values) as parameters.

foo(*[1, 2])
foo(1, 2, **{'f': 3})
['f: 3']

In some weird scenario you might need a bunch of arguments in your function. So instead of declaring all those parameters you could use **kwargs. And instead of writing every parameter on function call you could use arguments unpacking.

Also, you can use unpacking as a dictionary update.

pets = {'cat': 1, 'dog': 1}
{'cat': 1, 'dog': 1}
animals = {'pig': 2, 'cow': 1, **pets}
{'pig': 2, 'cow': 1, 'cat': 1, 'dog': 1}

My use case for this was filtering a Django query. I had a bunch of filters but they all had a few common ones. So my code looked something like this:

finished_filter = {
'cakes__status': CakeStatus.FINISHED, # much more like this
finished=Count('cakes', filter=Q(**finished_filter),
ready_to_ship=Count('cakes', filter=Q(**finished_filter, cakes__ready_to_ship=True)

You get the idea.

I think it’s a really powerful tool but as always, don’t abuse that power.