Functions

Functions are a way to organize code. They allow us to group related code together and reuse it throughout our program.

When calling a function, we are telling the program to execute the code inside the function, then return to where we were with the result of the function.

Defining Functions

Functions are defined using the def keyword.

def my_function():
    pass

Calling Functions

Functions are called using the function name followed by parentheses.

def my_function():
    pass


my_function()

Function Arguments

Functions can take arguments. Arguments are variables that are passed to the function when it is called.

def my_function(name):
    print("Hello, " + name + "!")


my_function("world")
# > Hello, world!

Trivia

In programming we differentiate between arguments and parameters. Parameters are the variables defined in the function definition. Arguments are the values passed to the function when it is called. In the example above, name is a parameter and "world" is an argument.

Positional and Keyword Arguments

Arguments can be passed by position or by name. Positional arguments are arguments that are passed by position. Keyword arguments are arguments that are passed by name.

def my_function(a, b, c):
    print(a, b, c)


my_function(1, 2, 3)
# > 1 2 3

my_function(a=1, b=2, c=3)
# > 1 2 3

my_function(1, b=2, c=3)
# > 1 2 3

Keyword arguments can not be passed before positional arguments.

def my_function(a, b, c):
    print(a, b, c)


my_function(a=1, 2, 3)
# > SyntaxError: positional argument follows keyword argument

Keyword arguments can be passed in any order.

def my_function(a, b, c):
    print(a, b, c)


my_function(c=3, a=1, b=2)
# > 1 2 3

Any parameter that is before a / will be a positional-only argument. Any parameter that is after a / will keyword-or-positional parameters. Any parameter that is after a * will be a keyword-only argument.

def my_function(a, b, /, c, d, *, e, f):
    print(a, b, c, d, e, f)


my_function(1, 2, 3, 4, e=5, f=6)
# > 1 2 3 4 5 6

my_function(1, 2, c=3, d=4, e=5, f=6)
# > 1 2 3 4 5 6

my_function(1, 2, 3, 4, 5, 6)
# > TypeError: my_function() takes 4 positional arguments but 6 were given

my_function(a=1, b=2, c=3, d=4, e=5, f=6)
# > TypeError: my_function() got some positional-only arguments passed as keyword arguments: 'a, b'

Note: Usually keyword-or-positional parameters and positional-only parameters cannot be followed after a parameter with a default value. However, if the parameter is keyword-only, then this is allowed.

def my_function(a, b, /, c=3, d=4, *, e, f):
    print(a, b, c, d, e, f)


my_function(1, 2, e=5, f=6)
# > 1 2 3 4 5 6

Default Arguments

Parameters can have default values. If a parameter has a default value, then it is optional when calling the function.

def my_function(name="world"):
    print("Hello, " + name + "!")


my_function()
# > Hello, world!

my_function("Python")
# > Hello, Python!

Positional-only parameters and keyword-or-positional parameters cannot follow parameters with default values.

def my_function(a, b=2, /, c, d=4, *, e, f):
    print(a, b, c, d, e, f)

# > SyntaxError: non-default argument follows default argument

Default values are evaluated when the function is defined, not when the function is called. You should always use immutable values as default values.

def my_function(a, b=[]):
    b.append(a)
    print(b)


my_function(1)
# > [1]

my_function(2)
# > [1, 2]

my_function(3)
# > [1, 2, 3]

Any expression is allowed as a default value (this also means that variables can be used as default values, but remember that the expression is evaluated when the function is defined, not when the function is called).

def my_function(a, b=2 + 2):
    print(a, b)


my_function(1)
# > 1 4

c = 3


def my_function(a, b=c):
    print(a, b)


my_function(1)
# > 1 3

c = 4

my_function(1)
# > 1 3

Return Values

Functions can return values using the return keyword.

def my_function():
    return 1


print(my_function())
# > 1

Functions can return multiple values by separating them with commas.

def my_function():
    return 1, 2, 3


a, b, c = my_function()

print(a, b, c)
# > 1 2 3

Note: If a function does not return a value, then it returns None.

Arbitrary Arguments

Functions can take an arbitrary number of arguments using the * operator, any positional argument when calling a function will be collected into the variable that follows the * operator.

def my_function(*args):
    print(args)


my_function(1, 2, 3)
# > (1, 2, 3)

To collect keyword arguments, use the ** operator.

def my_function(**kwargs):
    print(kwargs)


my_function(a=1, b=2, c=3)
# > {'a': 1, 'b': 2, 'c': 3}

Scope

Variables defined inside a function are local to that function. Variables defined outside a function are global. Variables defined outside a function can be accessed inside a function, but variables defined inside a function cannot be accessed outside that function.

def my_function():
    a = 1
    print(a)


my_function()


# > 1


def my_function():
    print(a)


a = 1

my_function()


# > 1


def my_function():
    b = 1


my_function()
print(b)
# > NameError: name 'b' is not defined

By default, variables from outer scope can only be manipulated or read, but not redefined, you can use the global and nonlocal keywords to change this behavior.

a = 0


def my_function():
    global a
    a = 1


print(a)
# > 0

my_function()
print(a)
# > 1
def my_function():
    a = 1

    def my_inner_function():
        nonlocal a
        a = 2

    my_inner_function()
    print(a)


my_function()
# > 2

Note: It is generally considered bad practice to use global variables.

Recursion

Functions are able to call themselves, this is called recursion.

def my_function(n):
    if n == 0:
        return 0
    return n + my_function(n - 1)


print(my_function(10))
# > 55

Functions are also able to call any other function.

def my_function(n):
    return n + 1


def my_other_function(n):
    return my_function(n) + 1


print(my_other_function(10))
# > 12