Accepting any number of arguments to a function

Related article:

Transcript:

How can you make a Python function that accepts any number of positional arguments?

Passing Arguments to a Function

Here's a function (called greet) that accepts a single argument (name):

>>> def greet(name):
...     print("Hello", name)
...

We can pass that argument in positionally:

>>> greet("Trey")
Hello Trey

Or as a keyword argument:

>>> greet(name="Trey")
Hello Trey

If we pass in 2 names or 3 names to this function, we'll get an error:

>>> greet("Trey", "Jo")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: greet() takes 1 positional argument but 2 were given
>>> greet("Trey", "Jo", "Ian")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: greet() takes 1 positional argument but 3 were given

We'd like to modify this function to accept any number of names (and to greet each name given).

Asterisks can pack multiple arguments

To accept any number of positional arguments we're going to need to use the * operator when defining this function. We'll replace name with *names:

>>> def greet(*names):
...     for name in names:
...         print("Hello", name)
...

That *names in our arguments tells Python that we want to capture all positional arguments given to this function, into a tuple, point the names variable to that tuple.

Now when we call greet with just one name, it works the same as before:

>>> greet("Trey")
Hello Trey

But we can also call it with multiple names to greet multiple people:

>>> greet("Trey", "Jo")
Hello Trey
Hello Jo
>>> greet("Trey", "Jo", "Ian")
Hello Trey
Hello Jo
Hello Ian

In fact, we can even call it with no names:

>>> greet()
>>>

That names tuple in the greet function is empty in this case so the loop does nothing. It's not an error to give zero arguments when you're using *.

Built-In function use argument packing

What are examples of functions that accept any number of positional arguments?

A number of Python's built-in functions actually work this way. For example, the zip function accepts any number of iterables to loop over at the same time:

>>> help(zip)

class zip(object)
 |  zip(*iterables) --> zip object

That *iterables in the definition of the zip function (zip class technically) means it accepts any number of iterables. When you give zip loop over all of those iterables at the same time.

The min and max functions, also accept any number of positional arguments:

min(arg1, arg2, *args, *[, key=func]) -> value

Note in that in the min documentation that arg1 and arg2 are before *args. It's possible to define your own functions like this. We can put other (required) positional arguments before all the remaining (optional) positional arguments that we're capturing.

You can also pass any number of arguments to the print function:

>>> help(print)

print(...)
    print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)

The documentation is a little bit different with print: it doesn't actually show that * operator (instead it shows ... to indicate that any number of values are accepted).

You can give any number of arguments to print and it will print all of them out with spaces between them:

>>> print(1, 2, 3, 4)
1 2 3 4

Summary

To make a function that accepts any number of arguments, you can use the * operator and then some variable name when defining your function's arguments. This lets Python know that when that function is called with any position arguments, they should all be captured into a tuple (which that variable will point to).

So if you'd like to make a function that accepts any number of positional arguments, use the * operator.