Python Arguments



*args and **kwargs - Collecting and Unpacking Arguments

Putting *args and/or **kwargs as the last items in our function definition's argument list allows that function to accept an arbitrary number of anonymous and/or keyword arguments.
Those arguments are called Keyword Arguments. Actually, they are place holders for multiple arguments, and they are useful especially when we need to pass a different number of arguments each time we call the function.
We may want to use *args when we're not sure how many arguments might be passed to our function, i.e. it allows us to pass an arbitrary number of arguments to our function.


Collecting arguments

>>> def f(*args):
        print(args)
       
>>> f()
()
>>> f(10)
(10,)
>>> f(10, 20, 30)
(10, 20, 30)
When the f() is called, Python collects all the positional arguments into a new tuple and assigns the variable args to that tuple. Since it is a normal tuple object, it can be indexed:
>>> def print_all(*args):
        for x in enumerate(args):
                print x
               
>>> print_all('A','b','b','a')
(0, 'A')
(1, 'b')
(2, 'b')
(3, 'a')
The ** is similar but it only works for keyword arguments. In other words, it collects them into a new dictionary. Actually, ** allows us to convert from keywords to dictionaries:
>>> def f(**kargs):
        print(kargs)
       
>>> f()
{}
>>> f(a=10, b=20)
{'a': 10, 'b': 20}
The keyword arguments is a special name=value syntax in function calls that specifies passing by name. It is often used to provide configuration options.
>>> def kargs_function(**kargs):
        for k,v in kargs.items():
                print (k,v)

>>>kargs_function(**{'uno':'one','dos':'two','tres':'three'})
('dos', 'two')
('tres', 'three')
('uno', 'one')
>>> 
>>> kargs_function(dos='two', tres='three', uno='one')
('dos', 'two')
('tres', 'three')
('uno', 'one')
>>>


Unpacking arguments

We can use the * or ** when we call a function. In other words, it unpacks a collection of arguments, rather than constructing a collection of arguments. In the following example, we pass five arguments to a function in a tuple and let Python unpack them into individual arguments:
>>> def f(a, b, c, d, e):
        print(a, b, c, d, e)
       
>>> args = (10, 20)
>>> args += (30, 40, 50))
>>> f(*args)
10 20 30 40 50
In the same way, the ** in a function call unpacks a dictionary of key/value pairs into separate keyword arguments:
>>> kargs = {'a':10, 'b':20, 'c':30}
>>> kargs['d']=40
>>> kargs['e']=50
>>> f(**kargs)
10 20 30 40 50
Also, with various combinations:
>>> f(*(10, 20), **{'d':40, 'e':50, 'c':30})
10 20 30 40 50
>>> f(10, *(20, 30), **{'d':40, 'e':50})
10 20 30 40 50
>>> f(10, c = 30, *(20,), **{'d':40, 'e':50})
10 20 30 40 50
>>> f(10, *(20,30), d=40, e=50)
10 20 30 40 50
>>> f(10, *(20,), c=30, **{'d':40, 'e':50})
10 20 30 40 50


Arbitrary function

In the code below, we support any function with any arguments by passing along whatever arguments that were sent in:
>>> def A_function(f, *args, **kargs):
        return f(*args, **kargs)

>>> def f(a, b, c, d, e):
        return a*b*c*d*e

>>> print(A_function(f, 10, 20, c=30, d=40, e=50))
12000000
When the code is run, arguments are collected by the A_function.


apply() - deprecated

f(*args, **kargs)         # newer call syntax: f(*sequence, **dict)
apply(f, args, kargs)     # deprecated built-in: apply(f, sequence, dict)
The following function accepts any number of positional or keyword arguments:
>>> def echo(*args, **kargs):
        print(args, kargs)

>>> echo(10, 20, a=30, b=40)
(10, 20) {'a': 30, 'b': 40}
If we use the apply():
>>> args = (10, 20)
>>> kargs = {'a':30, 'b':40}

>>> def echo(*args, **kargs):
        print(args, kargs)
       
>>> apply(echo, args, kargs)
((10, 20), {'a': 30, 'b': 40})
>>> echo(*args, **kargs)
((10, 20), {'a': 30, 'b': 40})

Comments