What Does ** (Double Star/Asterisk) and * (Star/Asterisk) Do For Parameters

What does ** (double star/asterisk) and * (star/asterisk) do for parameters?

The *args and **kwargs is a common idiom to allow arbitrary number of arguments to functions as described in the section more on defining functions in the Python documentation.

The *args will give you all function parameters as a tuple:

def foo(*args):
for a in args:
print(a)

foo(1)
# 1

foo(1,2,3)
# 1
# 2
# 3

The **kwargs will give you all
keyword arguments except for those corresponding to a formal parameter as a dictionary.

def bar(**kwargs):
for a in kwargs:
print(a, kwargs[a])

bar(name='one', age=27)
# name one
# age 27

Both idioms can be mixed with normal arguments to allow a set of fixed and some variable arguments:

def foo(kind, *args, **kwargs):
pass

It is also possible to use this the other way around:

def foo(a, b, c):
print(a, b, c)

obj = {'b':10, 'c':'lee'}

foo(100,**obj)
# 100 10 lee

Another usage of the *l idiom is to unpack argument lists when calling a function.

def foo(bar, lee):
print(bar, lee)

l = [1,2]

foo(*l)
# 1 2

In Python 3 it is possible to use *l on the left side of an assignment (Extended Iterable Unpacking), though it gives a list instead of a tuple in this context:

first, *rest = [1,2,3,4]
first, *l, last = [1,2,3,4]

Also Python 3 adds new semantic (refer PEP 3102):

def func(arg1, arg2, arg3, *, kwarg1, kwarg2):
pass

For example the following works in python 3 but not python 2:

>>> x = [1, 2]
>>> [*x]
[1, 2]
>>> [*x, 3, 4]
[1, 2, 3, 4]

>>> x = {1:1, 2:2}
>>> x
{1: 1, 2: 2}
>>> {**x, 3:3, 4:4}
{1: 1, 2: 2, 3: 3, 4: 4}

Such function accepts only 3 positional arguments, and everything after * can only be passed as keyword arguments.

Note:

  • A Python dict, semantically used for keyword argument passing, are arbitrarily ordered. However, in Python 3.6, keyword arguments are guaranteed to remember insertion order.
  • "The order of elements in **kwargs now corresponds to the order in which keyword arguments were passed to the function." - What’s New In Python 3.6
  • In fact, all dicts in CPython 3.6 will remember insertion order as an implementation detail, this becomes standard in Python 3.7.

What does the star and doublestar operator mean in a function call?

The single star * unpacks the sequence/collection into positional arguments, so you can do this:

def sum(a, b):
return a + b

values = (1, 2)

s = sum(*values)

This will unpack the tuple so that it actually executes as:

s = sum(1, 2)

The double star ** does the same, only using a dictionary and thus named arguments:

values = { 'a': 1, 'b': 2 }
s = sum(**values)

You can also combine:

def sum(a, b, c, d):
return a + b + c + d

values1 = (1, 2)
values2 = { 'c': 10, 'd': 15 }
s = sum(*values1, **values2)

will execute as:

s = sum(1, 2, c=10, d=15)

Also see section 4.7.4 - Unpacking Argument Lists of the Python documentation.


Additionally you can define functions to take *x and **y arguments, this allows a function to accept any number of positional and/or named arguments that aren't specifically named in the declaration.

Example:

def sum(*values):
s = 0
for v in values:
s = s + v
return s

s = sum(1, 2, 3, 4, 5)

or with **:

def get_a(**values):
return values['a']

s = get_a(a=1, b=2) # returns 1

this can allow you to specify a large number of optional parameters without having to declare them.

And again, you can combine:

def sum(*values, **options):
s = 0
for i in values:
s = s + i
if "neg" in options:
if options["neg"]:
s = -s
return s

s = sum(1, 2, 3, 4, 5) # returns 15
s = sum(1, 2, 3, 4, 5, neg=True) # returns -15
s = sum(1, 2, 3, 4, 5, neg=False) # returns 15

what does double star followed by variable name mean in python?

It refers to all keyword arguments passed to the function that aren't in the method definition. For example:

>>> def foo(arg, **kwargs):
... print kwargs
...
>>> foo('a', b="2", c="3", bar="bar")
{'c': '3', 'b': '2', 'bar': 'bar'}

It is similar to just using one asterisk, which refers to all non-keyword arguments:

>>> def bar(arg, *args):
... print args
...
>>> bar(1, 2, 3, 'a', 'b')
(2, 3, 'a', 'b')

You can combine these(and people often do)

>>> def foobar(*args, **kwargs):
... print args
... print kwargs
...
>>> foobar(1, 2, a='3', spam='eggs')
(1, 2)
{'a': '3', 'spam': 'eggs'}

Star (*) as an argument in python function

The * indicates the end of the positional arguments. Every argument after that can only be specified by keyword. This is defined in PEP 3102

>>> def foo1(a, b=None):
... print(a, b)
...
>>> def foo2(a, *, b=None):
... print(a, b)
...
>>> foo1(1, 2)
1 2
>>> foo2(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo1() takes 1 positional argument but 2 were given
>>> foo2(1, b=2)
1 2

What do * (single star) and / (slash) do as independent parameters?

There is a new function parameter syntax / to indicate that some function parameters must be specified positionally and cannot be used as keyword arguments.[This is new in Python 3.8]

Documentation specifies some of the use cases/benefits of positional-only parameters

  1. It allows pure Python functions to fully emulate behaviors of
    existing C coded functions. For example, the built-in pow()
    function does not accept keyword arguments:

    def pow(x, y, z=None, /):
    "Emulate the built in pow() function"
    r = x ** y
    return r if z is None else r%z
  2. Another use case is to preclude keyword arguments when the parameter
    name is not helpful. For example, the builtin len() function has
    the signature len(obj, /). This precludes awkward calls such as:

    len(obj='hello')  # The "obj" keyword argument impairs readability
  3. A further benefit of marking a parameter as positional-only is that
    it allows the parameter name to be changed in the future without
    risk of breaking client code. For example, in the statistics module,
    the parameter name dist may be changed in the future. This was made
    possible with the following function specification:

    def quantiles(dist, /, *, n=4, method='exclusive')
    ...

Where as * is used to force the caller to use named arguments. This is one of the use case of named arguments.

So, given the method,

def func(self, param1, param2, /, param3, *, param4, param5):
print(param1, param2, param3, param4, param5)

It must called with

obj.func(10, 20, 30, param4=50, param5=60)

or

obj.func(10, 20, param3=30, param4=50, param5=60)

ie,

  1. param1, param2 must be specified positionally.
  2. param3 can be called either with positional or keyword.
  3. param4 and param5 must be called with keyword argument.

DEMO:

>>> class MyClass(object):
... def func(self, param1, param2, /, param3, *, param4, param5):
... return param1, param2, param3, param4, param5
...
>>> obj = MyClass()
>>>
>>> assert obj.func(10, 20, 30, param4=40, param5=50), obj.func(
... 10, 20, param3=30, param4=40, param5=50
... )


Related Topics



Leave a reply



Submit