In Day 9 we'll bring positional-only and keyword-only arguments together in a single function

Have a look at this example

—> Which of these options will raise errors?

You can refer to the threads from the last two days if you need to refresh your memories…

/1

Let's go through all four function calls one at a time

# 1.

All the arguments are positional arguments in this call

This leads to an error

/2

The part of the function signature which leads to this problem is the asterisk *

Any parameter after the asterisk must be matched to positional-only arguments

/3

Therefore, this function takes at most two positional arguments

This is why the error message says:

greet() takes 2 positional arguments but 4 were given

/4

You can confirm that it's the asterisk that causes this error by removing the asterisk and see what happens:

/5

Let's move to the next function call

# 2.

The first two arguments are positional arguments:
`"Hello"` is assigned to `greeting`
`3` is assigned to `repeat`

The third and fourth are keyword arguments:
`host="James"`
`guest="Claire"`

/6

Only the last two arguments must be keyword arguments. Therefore, this function call is perfectly fine

/7

Let's look at the next one

# 3.

Only the first argument in a positional argument:
`"Hello"` is assigned to `greeting`

The rest are all keyword arguments:
`repeat=3`
`host="James"`
`guest="Claire"`

This also works. Let's see why

/8

The last two arguments must be keyword arguments since `host` and `guest` come after the asterisk * in the function definition

`repeat` is sandwiched between the forward slash / and the asterisk * in the function signature

/9

This means the argument is neither a keyword-only argument nor a positional-only argument

It's not a keyword-only argument because `repeat` comes _before_ the asterisk *

It's not a positional-only argument because `repeat` comes _after_ the forward slash /

/10

Therefore the argument which matches `repeat` is a "normal" one. It can be passed either as a positional argument or as a keyword argument

The user who's calling the function can choose

/11

Let's look at the final example:

# 4.

All arguments are keyword arguments in this case

This leads to an error:

greet() got some positional-only arguments passed as keyword arguments: 'greeting'

/12

In the function definition, `greeting` is before the forward slash /

Therefore, the argument assigned to `greeting` must be a positional-only argument

/13

In summary, you can define a function to have some positional-only arguments, some keyword-only arguments, and some arguments that could be either positional or keyword ones

/14

Parameters before the forward slash / : must use positional-only arguments

Parameters after the asterisk * : must use keyword-only arguments

Parameter in between / and * : can be either positional arguments or keyword arguments

/15

I sometimes struggle to recall which of the / or * symbols does what

To remember which is which, you can note that the asterisk * is the same as used when creating *args

In fact, you could replace * with *args and the function will work in a similar manner in this case

/16

*args is "mopping up" all remaining positional arguments before the keyword arguments – keyword arguments always come after positional ones in a function call

In this example, there are no additional positional arguments to "mop up", so `args` is an empty tuple

/17

Note that if using *args, all arguments before the *args need to be positional so the / is no longer needed in this case

/18

Just using * instead of *args takes care of this scenario and therefore forces the remaining parameters to be used with keyword-only arguments

/19

We'll now move on to the final part of this series on Python functions and talk about making functions neater, more readable, and possible minimise bugs by use type annotations and docstrings

/20