Make Arguments Optional


Problem to Solve

I want to write a function with an argument that the user can ignore unless they want to override the "normal" value with something else.


Recipe

Step 1: Define the Function With Default Values

You can make any one of a function's arguments optional — meaning that calling code can either specify a value for it or omit it entirely — by supplying a default value in the function signature:

def function_name(arg_1, arg_2=default_value_2):
    """ Template for a function that has one required and one optional argument.
    """
    pass # Replace these two lines with actual code that does
    pass # something with arg_1 and arg_2.
    return result

Replace default_value_2 with the specific value you want the function to use for the value of arg_2 if the calling code doesn't provide one. - You can specify a numeric literal, a string literal, a variable, or a more complicated python expression. - However, mind that the variable or expression will be evaluated only once, at the time the function definition is sent to the interpreter, and NOT every time the function is called. - A literal number, string, or boolean value is by far the most common kind of default value.

Step 2: Call the Function With or Without the Optional Argument

When calling a function with an optional argument, you can either provide a value for the optional argument or omit it. If you omit it, the function will use the default value specified in the function definition.

Both of these function calls are valid:

a = function_name(val_1, val_2)
b = function_name(other_val_1)

Elaboration: Mixing and Matching Multiple Optional Arguments

If more than one argument is optional, and calling code provides fewer than the maximum number of values, which optional arguments get the passed values and which use default values?

Any values provided by the calling code will be assigned to arguments from left to right according to the function signature, unless told otherwise by explicit argument names. Consider this function definition:

def function_name(x=0, y=0):
    """ Template for a function that has two optional arguments.
    """
    pass # Do something with x and y.
    return result

The function can be called in any of these ways:

out_1 = function_name()      # Sets x to 0 and y to 0
out_2 = function_name(1)     # Sets x to 1 and y to 0
out_3 = function_name(1, 2)  # Sets x to 1 and y to 2
out_4 = function_name(y=2)   # Sets x to 0 and y to 2

The following are also legitimate, but unnecessary. The first two might be worth writing for the sake of clarity; the third is simply confusing.

out_5 = function_name(x=1)       # Sets x to 1 and y to 0
out_6 = function_name(x=1, y=2)  # Sets x to 1 and y to 2
out_7 = function_name(y=2, x=1)  # Sets x to 1 and y to 2

Side note: It is possible to define a function that can accept any number of optional arguments, either with or without argument names in the calling code. However, that requires the use of collections that we have not yet looked at.


Example

import math
def projectile_range(v0, theta, h=0, g=9.81):
    """ Calculate the range of a ballistic projectile without air resistance, launched
        from an arbitrary height above the ground.

        Parameters:
        - v0: The projectile's initial velocity (m/s)
        - theta: the projectile's launch angle above horizontal (degrees)
        - h: the launch height above the ground (m), defaults to zero
        - g: the local gravitational constant (m/s**2), defaults to Earth

        Returns: the horizontal distance the projectile travels, in meters, before
            hitting the ground.
    """
    # Convert angle from degrees to radians.
    theta_rad = math.radians(theta)

    # Calculate range using the standard formula derived in intro physics.
    range_ = (v0 * math.cos(theta_rad) / g) * (
        v0 * math.sin(theta_rad) + math.sqrt((v0 * math.sin(theta_rad))**2 + 2 * g * h)
    )

    return range_

Note: This uses range_ instead of range for the name of the returned variable because range is the name of a built-in python function that we don't want to collide with. Appending an underscore is a common way of working around conflicts between desirable variable names and already-existing python names.

Usage:

v, theta, h = 85, 25, 10
print("On level ground and in the absence of air drag, the horizontal distance traveled")
print(f"by a ballistic projectile fired with muzzle speed {v} m/s at an angle of {theta}")
print(f"degrees above the horizontal is {projectile_range(v, theta):.2f} meters.")
print()
print(f"If the projectile is launched from a height of {h} meters above the ground,")
print(f"the horizontal distance traveled is {projectile_range(v, theta, h):.2f} meters.")
print()
g_moon = 1.625  # Moon's gravity is about 1/6th of Earth's.
print("If launched on the moon from ground level, the distance traveled would be")
print(f"{projectile_range(v, theta, g=g_moon):.2f} meters.")