KEYWORD ARGUMENTS WITH *

S. Shruti Sharada
Techiepedia
Published in
3 min readAug 9, 2020

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!

References:

  1. https://automatetheboringstuff.com/2e/chapter3/
  2. https://treyhunner.com/2018/04/keyword-arguments-in-python/
  3. https://www.python.org/dev/peps/pep-0468/

--

--

S. Shruti Sharada
Techiepedia

Hey! I joined Medium 3 years ago, as a final year student, and I write blogs on anything that interests me. I also write blogs for the publication, Techiepedia.