(*, /) Beyond Multiplication and Division - Part2

(*, /) Beyond Multiplication and Division - Part2

How to use the forward slash and asterisk together in a function definition to specify keyword and positional arguments; also a look at `*args`.

In the previous article, I introduced the special powers of the asterisk * and the forward slash /. If you missed it, you can check it out here: Part 1. In this article, we will be looking at how to use the asterisk and forward slash together in a function definition. We'll also say hi to our new friend called *args.

We go again

To use both operators in a function definition we need a refresher on how they work separately (in function definition).

If you remember when an asterisk * is used in a function definition it causes all the parameters on its left to be passed as either positional or keyword arguments and those on its right can only be passed as keyword arguments. For the / it causes all parameters on its left to be passed only as positional arguments, while those on its right can be either of the two types of arguments.

Now to combine them in a function definition we must always put the slash / before the asterisk *. This is how you should do it:

def display(a, b, /, c, d, *, e):
    """a function that prints all arguments passed to it"""
    print(a, b, c, d, e)

Why should the slash come before the asterisk? It's simple. In Python positional arguments should always be placed before keyword arguments, like this:

imaginaryFunction("Mariam", "Zombo", age=10, school="xyz")

...also in Python, you do not mix positional and keyword arguments like this:

imaginaryFunction("Mariam", age=10, "Zombo", school="xyz")
SyntaxError: positional argument follows keyword argument

As a result of the order in which Python requires arguments to be passed, the forward slash / comes before the asterisk *.

To understand what I just said, let's combine these operators, and specify the type of arguments each takes on its left and right.

In this example, for simplicity:
positional-arguments is positional
keyword-arguments is keyword

// arguments for the slash
positional / positional and keyword

// arguments for asterisk
positional and keyword * keyword

COMBINING THEM

positional / positional and keyword * keyword

As seen above putting the slash first doesn't break Python's arguments rules. All positional arguments come before the keyword arguments. Also, the rules governing each operator are satisfied.

But if we had put the asterisk first, we would have this kind of structure:

positional and keyword * keyword and positional / positional and keyword

Looking at this, you will see that aside from breaking the argument rules, these operators are not being used as they should be used. For example, we have both positional and keyword arguments placed after the asterisk *, which is incorrect.

Let's try to use our display function to better understand all that has been said (try to run this code on your machine).

def display(a, b, /, c, d, *, e):
    """a function that prints all arguments passed to it"""
    print(a, b, c, d, e)

display("letterA", "letterB", "letterC", "letterD", e="letterE")
display("letterA", "letterB", "letterC", d="letterD", e="letterE")
display("letterA", "letterB", c="letterC", d="letterD", e="letterE")
letterA letterB letterC letterD letterE
letterA letterB letterC letterD letterE
letterA letterB letterC letterD letterE

Note that the arguments between / and * can be either positional or keyword arguments, but when using both types of arguments between the operators always put the positional argument(s) first.


Arrrrrrrggggss!!!!

This is args - *args, consider it as a store for any extra positional argument that is passed to a function. That is to say, it is where all positional arguments that are not defined in the function but are passed to it, get stored or placed in.

Here is what the previous paragraph means. If I define a function like this:

def say(name, day, *args):
    """a function that says a nice greeting"""
    print(f"Hello {name}! How is your {day} going?")
    if len(args) > 0:
        for each in args:
            print(f"Extra: {each}")

When calling this function, if by any means you accidentally or deliberately add one or more positional arguments they will be stored in *args.

How? *args is a tuple, that's how it can store those arguments; it stores them as its elements, so you can iterate through it to access all positional arguments stored in it.

say("M'mah", "Wednesday", "AWMS")
Hello M'mah! How is your Wednesday going?
Extra: AWMS

Some guidelines for using *args:

  • If you want to add parameters after it, they must all be passed as keyword arguments.

  • For *args to store extra positional arguments, only put positional arguments on its left. If you add a keyword argument on its left, it will not store any extra positional argument.

      def demo(first, second, *args, third):
          print(first, second, third)
    
      demo("hi,", "hi from extra,", second="hi again,", third="the last hi")
    
      Traceback (most recent call last):
        File "<pyshell#4>", line 1, in <module>
          demo("hi,", "hi from extra,", second="hi again,", third="the last hi")
      TypeError: demo() got multiple values for argument 'second'
    
      # adding only positional arguments on its left
      demo("hi,", "hi from extra,", "hi again,", third="the last hi")
    
      hi, hi from extra, hi again, the last hi
    
  • You can iterate through it like a Python tuple.

  • When iterating through *args you should drop the *, leaving it as args.

      # ...
          for each in args:
              #...
    

Why include *args ?

Because it works as a special type of *. So you can use *args in place of * whenever you want to make room for undefined arguments.


Conclusion

There is more to *args and the other operators, but this is where I will stop for now, I might write another article to explain a little more about them. Meanwhile, I encourage you to do more research on and practice using them in your code for better understanding.

Thanks for reading and if you find this article informative kindly give it a like and share it with others.

Thank you.