Arguments in Practice

Positional arguments are required

Positional arguments are all required, and must be given in the order they are declared.

For example, this function doesn’t do what we expected, because we passed in our arguments in the wrong order.

>>> def say_greeting(name, greeting):
...     print(f"{greeting}, {name}.")
>>> say_greeting("Hello!", "Nina")
Nina, Hello!.

Keyword arguments with default values

Functions can accept two types of named arguments, ones without default values referred to as args, required arguments, positional arguments, or non-default values. Providing values for these are required.

Arguments that have default values are called keyword arguments, kwargs, or optional arguments. The default values you provide can optionally be overwritten.

Let’s see this in practice, by writing two functions that print out a greeting. One function will have a default argument to make things easier for us.

Without default arguments

Now, in the REPL, let’s try calling our function with no default arguments:

>>> # No Default arguments
>>> say_greeting("Good Day", "Nina")
Good Day, Nina.

Using default arguments

Let’s make a new function, say_greeting_with_default that accepts three arguments – name, and now the optional arguments greeting and punctuation. If greeting is not passed in, it will default to Hello, and punctuation will default to !

In the REPL:

>>> # With Default Arguments
>>> def say_greeting_with_default(name, greeting="Hello", punctuation="!"):
...     print(f"{greeting}, {name}{punctuation}")
>>> say_greeting_with_default("Nina")
Hello, Nina!
>>> say_greeting_with_default("Nina", "Good Day")
Good Day, Nina!

Order matters!

A function can accept all of one type or the other, but arguments need to go in a specific order.

All of the required arguments go first. They are then followed by the optional keyword arguments.

What happens when we try to define our arguments with keyword arguments defined first? If you guessed a SyntaxError, you’re correct!

>>> def say_greeting_bad(greeting="Hello", name):
...     print("Oops, this won't work!")
  File "<stdin>", line 1
SyntaxError: non-default argument follows default argument

Calling functions with arguments

There are a few important things to know about calling functions with arguments.

Arguments without defaults are required!

Arguments without default values are required by Python. Otherwise your function wouldn’t know what to do! If you don’t pass in all the required (positional) arguments, you’ll get a TypeError.

>>> def say_greeting(name, greeting):
...     print(f"{greeting}, {name}.")
>>> say_greeting("Nina")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: say_greeting() missing 1 required positional argument: 'greeting'

You can pass in none, some, or all of the keyword arguments.

If your function takes keyword arguments, you can provide zero, one, or all of them when you call it. You don’t need to pass these arguments in order if you provide their label when calling the function.

>>> def create_query(language="JavaScipt", num_stars=50, sort="desc"):
...     return f"language:{language} num_stars:{num_stars} sort:{sort}"
>>> create_query()
'language:JavaScipt num_stars:50 sort:desc'
>>> create_query(language="Ruby")
'language:Ruby num_stars:50 sort:desc'
>>> create_query(num_stars=1, language="Python", sort="asc")
'language:Python num_stars:1 sort:asc'

You can pass in required parameters by keyword.

Even if your function arguments don’t have keyword arguments with defaults, you can still pass values in to the function by name. This is especially helpful if you want to be extra clear about what you’re passing in.

Note that here, the arguments don’t need to be passed to the function in the order that they’re defined, because they’re labeled.

>>> def say_greeting(name, greeting):
...     print(f"{greeting}, {name}.")
>>> say_greeting("Nina", "Hello")
Hello, Nina.
>>> say_greeting(greeting="Bonjour", name="Max")
Bonjour, Max.

Arguments Danger Zone

Never use mutable types, like lists as a default argument.

Remember to never use an empty list as a default value to a function.

Why? Because it won’t work like you’d expect it to.

>>> # Don't do this!
>>> def add_five_to_list(my_list=[]):
...     my_list.append(5)
...     return my_list
>>> # This works like we expected it to.
>>> add_five_to_list()
>>> # Huh?
>>> add_five_to_list()
[5, 5]
>>> # We see that the original `my_list` is still being modified.
>>> add_five_to_list()
[5, 5, 5]

If you need to use a mutable type like a list as a default, use a marker instead. We’ll cover this technique later in this chapter.

In Python, default arguments are evaluated only once – when the function is defined. Not each time the function is called. That means if you use a mutable data type like a list as a default argument, it will be reused each time that the function is called.

Naming Functions and Arguments

Because Python is a dynamic language (sometimes called duck-typed) we use names as cues for what our function does, the arguments it accepts, and the values it returns.

This is especially important because we generally don’t declare types for our programs when we’re first starting out. Note: Python does support Type hinting, but it’s more of an intermediate feature. Make sure you have the basics down before learning more about it.

Try to avoid single character names for your functions and variables, unless they have meaning in math.

For example, in this function, x and y are common names used when referring to points, so it’s OK to use single-letter names in this scenario.

def add_points(x1, y1, x2, y2):
    return x1 + x2, y1 + y2

For sequences, like lists, it’s appropriate to name them in the plural.

For example, I’d expect a variable called name to be a single string, and a variable called names to be a list of strings.

A great resource to help you figure out the best naming conventions to use in your production Python code is a talk by Brandon Rhodes, called “The Naming of Ducks: Where Dynamic Types Meet Smart Conventions”.