Reputation: 6056
What does a bare asterisk in the parameters of a function do?
When I looked at the pickle module, I see this:
pickle.dump(obj, file, protocol=None, *, fix_imports=True)
I know about a single and double asterisks preceding parameters (for variable number of parameters), but this precedes nothing. And I'm pretty sure this has nothing to do with pickle. That's probably just an example of this happening. I only learned its name when I sent this to the interpreter:
>>> def func(*):
... pass
...
File "<stdin>", line 1
SyntaxError: named arguments must follow bare *
If it matters, I'm on python 3.3.0.
Upvotes: 509
Views: 108381
Reputation: 51037
Semantically, it means the arguments following it are keyword-only, so you will get an error if you try to provide an argument without specifying its name. For example:
>>> def f(a, *, b):
... return a + b
...
>>> f(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() takes 1 positional argument but 2 were given
>>> f(1, b=2)
3
Pragmatically, it means you have to call the function with a keyword argument. It's usually done when it would be hard to understand the purpose of the argument without the hint given by the argument's name.
Compare e.g. sorted(nums, reverse=True)
vs. if you wrote sorted(nums, True)
. The latter would be much less readable, so the Python developers chose to make you to write it the former way.
One other specific reason to force an argument to be keyword-only (or positional-only, with a /
) is to ensure consistent usage of an @lru_cache
. Decorators like @lru_cache
don't see that f(1, 2)
and f(1, b=2)
provide the same arguments, so if both calls are made in the same program, you won't get the full benefit of caching. For example:
>>> from functools import lru_cache
>>> @lru_cache
... def f(a, b):
... print(f'Non-cached call with {a=}, {b=}')
... return a + b
...
>>> f(1, 2)
Non-cached call with a=1, b=2
3
>>> f(1, 2) # cached result is used
3
>>> f(1, b=2) # cached result is not used
Non-cached call with a=1, b=2
3
So there is a practical benefit of forcing all calls to pass the arguments the same way.
Upvotes: 77
Reputation: 10718
Suppose you have function:
def sum(a,key=5):
return a + key
You can call this function in 2 ways:
sum(1,2)
or sum(1,key=2)
Suppose you want function sum
to be called only using keyword arguments.
You add *
to the function parameter list to mark the end of positional arguments.
So function defined as:
def sum(a,*,key=5):
return a + key
may be called only using sum(1,key=2)
Upvotes: 32
Reputation: 5587
def func(*, a, b):
print(a)
print(b)
func("gg") # TypeError: func() takes 0 positional arguments but 1 was given
func(a="gg") # TypeError: func() missing 1 required keyword-only argument: 'b'
func(a="aa", b="bb", c="cc") # TypeError: func() got an unexpected keyword argument 'c'
func(a="aa", b="bb", "cc") # SyntaxError: positional argument follows keyword argument
func(a="aa", b="bb") # aa, bb
the above example with **kwargs
def func(*, a, b, **kwargs):
print(a)
print(b)
print(kwargs)
func(a="aa",b="bb", c="cc") # aa, bb, {'c': 'cc'}
Upvotes: 78
Reputation: 76837
While the original answer answers the question completely, just adding a bit of related information. The behaviour for the single asterisk derives from PEP-3102
. Quoting the related section:
The second syntactical change is to allow the argument name to
be omitted for a varargs argument. The meaning of this is to
allow for keyword-only arguments for functions that would not
otherwise take a varargs argument:
def compare(a, b, *, key=None):
...
In simple english, it means that to pass the value for key, you will need to explicitly pass it as key="value"
.
Upvotes: 125
Reputation: 39538
Bare *
is used to force the caller to use named arguments - so you cannot define a function with *
as an argument when you have no following keyword arguments.
See this answer or Python 3 documentation for more details.
Upvotes: 439