KEYWORD ARGUMENTS WITH *
Why use *
You can create a function that accepts any number of positional arguments as well as some keyword-only arguments by using the *
operator to capture all the positional arguments and then specify optional keyword-only arguments after the *
capture.
Here’s an example:
def product(*numbers, initial=1):
total = initial
for n in numbers:
total *= n
return total
Note: If you haven’t seen that *
syntax before, *numbers
captures all positional arguments given to the product
function into a tuple which the numbers
variable points to.
The initial
argument in the above function must be specified as a keyword argument:
>>> product(4, 4)
16
>>> product(4, 4, initial=1)
16
>>> product(4, 5, 2, initial=3)
120
Note that while initial
has a default value, you can also specify required keyword-only arguments using this syntax:
def join(*iterables, joiner):
if not iterables:
return
yield from iterables[0]
for iterable in iterables[1:]:
yield joiner
yield from iterable
That joiner
variable doesn’t have a default value, so it must be specified:
>>> list(join([1, 2, 3], [4, 5], [6, 7], joiner=0))
[1, 2, 3, 0, 4, 5, 0, 6, 7]
>>> list(join([1, 2, 3], [4, 5], [6, 7], joiner='-'))
[1, 2, 3, '-', 4, 5, '-', 6, 7]
>>> list(join([1, 2, 3], [4, 5], [6, 7]))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: join() missing 1 required keyword-only argument: 'joiner'
Keyword-only arguments without positional arguments
What if you want to accept keyword-only arguments without also accepting unlimited positional arguments?
If you want to accept keyword-only arguments and you’re not using a *
to accept any number of positional arguments, you can use a *
without anything after it.
For example here’s a modified version of Django’s django.shortcuts.render
function:
def render(request, template_name, context=None, *, content_type=None, status=None, using=None):
content = loader.render_to_string(template_name, context, request, using=using)
return HttpResponse(content, content_type, status)
Unlike Django’s current implementation of render
, this version disallows calling render
by specifying every argument positionally. The content_type
, status
, and using
arguments must be specified by their name
.
>>> render(request, '500.html', {'error': error}, status=500)
<HttpResponse status_code=500, "text/html; charset=utf-8"> >>>
render(request, '500.html', {'error': error}, 500)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: render() takes from 2 to 3 positional arguments but 4 were given
Just like with unlimited positional arguments, these keyword arguments can be required. Here’s a function with four required keyword-only arguments:
from random import choice, shuffle
UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
LOWERCASE = UPPERCASE.lower()
DIGITS = "0123456789"
ALL = UPPERCASE + LOWERCASE + DIGITS
def random_password(*, upper, lower, digits, length):
chars = [
*(choice(UPPERCASE) for _ in range(upper)),
*(choice(LOWERCASE) for _ in range(lower)),
*(choice(DIGITS) for _ in range(digits)),
*(choice(ALL) for _ in range(length-upper-lower-digits)),
]
shuffle(chars)
return "".join(chars)
This function requires all of its arguments to be specified using their name:
>>> random_password(upper=1, lower=1, digits=1, length=8)
'oNA7rYWI'
>>> random_password(upper=1, lower=1, digits=1, length=8)
'bjonpuM6'
>>> random_password(1, 1, 1, 8)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: random_password() takes 0 positional arguments but 4 were given
Requiring arguments to be named can make calls to our function much clearer.
The purpose of this function call:
>>> password = random_password(upper=1, lower=1, digits=1, length=8)
Is much more obvious than this one:
>>> password = random_password(1, 1, 1, 8)
So, when defining a new function, stop to think about which arguments should always be specified as keyword arguments when calling your function. Consider using the *
operator to require those arguments be specified as keyword arguments.
Read My Keyword Arguments.Do Read My Next Blog as well(2/3).Coming Soon!