Python Functions

Functions are the first step to code reuse.

They allow you to define a reusable block of code that can be used repeatedly in a program.

Python provides several built-in functions such as print() and len(), but you can also define your own functions to use within your programs.

Syntax

The basic syntax for a Python function definition is:

Python Function Syntax

Create a Function

To define a Python function, use def keyword.

Here’s the simplest possible function that prints ‘Hello, World!’ on the screen.

Example:

def hello():
    print('Hello, World!')

Call a Function

The def statement merely creates a function but does not call it.

After the def has run, you can can call (run) the function by adding parentheses after the function’s name.

Example:

def hello():
    print('Hello, World!')
  
hello()     # Hello, World!

Pass Arguments

You can send information to a function by passing values, known as arguments.

Arguments are declared after the function name in parentheses.

When you call a function with arguments, the values of those arguments are copied to their corresponding parameters inside the function.

Example: Pass single argument to a function

def hello(name):
    print('Hello,', name)

hello('Bob')    # Hello, Bob
hello('Sam')    # Hello, Sam

You can send as many arguments as you like, separated by commas ,.

Example: Pass two arguments

def func(name, job):
    print(name, 'is a', job)

func('Bob', 'developer')    # Bob is a developer

Types of Arguments

Python handles function arguments in a very flexible manner, compared to other languages.

It supports multiple types of arguments in the function definition. Here’s the list:

  • Positional Arguments
  • Keyword Arguments
  • Default Arguments
  • Variable Length Positional Arguments (*args)
  • Variable Length Keyword Arguments (**kwargs)

Positional Arguments

The most common are positional arguments, whose values are copied to their corresponding parameters in order.

Example:

def func(name, job):
    print(name, 'is a', job)

func('Bob', 'developer')    # Bob is a developer

The only downside of positional arguments is that you need to pass arguments in the order in which they are defined.

Example:

def func(name, job):
    print(name, 'is a', job)

func('developer', 'Bob')    # developer is a Bob

Keyword Arguments

To avoid positional argument confusion, you can pass arguments using the names of their corresponding parameters.

In this case, the order of the arguments no longer matters because arguments are matched by name, not by position.

Example: Keyword arguments can be put in any order

def func(name, job):
    print(name, 'is a', job)

func(name='Bob', job='developer')   # Bob is a developer

func(job='developer', name='Bob')   # Bob is a developer

It is possible to combine positional and keyword arguments in a single call. If you do so, specify the positional arguments before keyword arguments.

Default Arguments

You can specify default values for arguments when defining a function.

The default value is used if the function is called without a corresponding argument.

In short, defaults allow you to make selected arguments optional.

Example: Set default value ‘developer’ to a ‘job’ parameter

def func(name, job='developer'):
    print(name, 'is a', job)

func('Bob', 'manager')    # Bob is a manager

func('Bob')               # Bob is a developer

Variable Length Arguments (*args and **kwargs)

Variable length arguments are useful when you want to create functions that take unlimited number of arguments.

Unlimited in the sense that you do not know beforehand how many arguments can be passed to your function by the user.

This feature is often referred to as varargs.

*args

When you prefix a parameter with an asterisk * , it collects all the unmatched positional arguments into a tuple.

Because it is a normal tuple object, you can perform any operation that a tuple supports, like indexing, iteration etc.

Following function prints all the arguments passed to the function as a tuple.

Example:

def print_arguments(*args):
    print(args)

print_arguments(1, 54, 60, 8, 98, 12)

Output:

(1, 54, 60, 8, 98, 12)

You don’t need to call this keyword parameter args, but it is standard practice.

**kwargs

The ** syntax is similar, but it only works for keyword arguments.

It collects them into a new dictionary, where the argument names are the keys, and their values are the corresponding dictionary values.

Example:

def print_arguments(**kwargs):
    print(kwargs)

print_arguments(name='Bob', age=25, job='dev')

Output:

{'name': 'Bob', 'age': 25, 'job': 'dev'}

Return Value

To return a value from a function, simply use a return statement.

Once a return statement is executed, nothing else in the function body is executed.

Example: Return sum of two values

def sum(a, b):
    return a + b

x = sum(3, 4)
print(x)  # 7

If you do not include any return statement, it automatically returns None. So, a python function always returns a value.

Return Multiple Values

Python has the ability to return multiple values, something missing from many other languages.

You can do this by separating return values with a comma.

Example: Return addition and subtraction of two arguments

def func(a, b):
    return a+b, a-b

result = func(3, 2)

print(result)    # (5, 1)

When you return multiple values, Python actually packs them in a single tuple and returns it.

You can then use multiple assignment to unpack the parts of the returned tuple.

Example: Unpack returned tuple

def func(a, b):
    return a+b, a-b

add, sub = func(3, 2)

print(add)      # 5
print(sub)      # 1

Docstring

You can attach documentation to a function definition by including a string literal just after the function header.

Docstrings are usually triple quoted to allow for multi-line descriptions.

Example: Add a docstring to a function

def hello():
    """This function prints
       message on the screen"""  
    print('Hello, World!')

To print a function’s docstring, use the Python help() function and pass the function’s name.

Example: Print docstring in rich format

help(hello)

Output:

Help on function hello in module __main__:

hello()
    This function prints
    message on the screen

You can also access the docstring through __doc__ attribute of the function.

Example: Print docstring in a raw format

print(hello.__doc__)

Output:

This function prints message on the screen

Nested Functions

A Nested function is a function defined within other function.

They are useful when performing complex task multiple times within another function, to avoid loops or code duplication.

Example:

def outer(a, b):
    def inner(c, d):
        return c + d
    return inner(a, b)

result = outer(2, 4)

print(result)       # 6

A nested function can act as a closure.

Recursion

A recursive function is a function that calls itself and repeats its behavior until some condition is met to return a result.

In below example, findSum() is a recursive function that calls itself (recurse). We use the variable num as the data, which decrements (-1) every time we recurse. The recursion ends when it becomes 0.

Example: Find sum of numbers from 0 to 5, using recursive function

def findSum(num):
  if num:
    return num + findSum(num-1)
  else:
    return 0

x = findSum(5)
print(x)  # 15

Assigning Functions to Variables

When Python runs a def statement, it creates a new function object and assigns it to the function’s name.

You can assign a different name to it anytime and call through the new name.

Example: Assign a different name to ‘hello’ function and call through the new name

def hello():
    print('Hello, World!')
  
hi = hello
hi()    # Hello, World!

You can use this feature to implement jump tables.

Jump tables are dictionaries of functions to be called on demand.

Example: Create a dictionary of functions

def findSquare(x):
    return x ** 2

def findCube(x):
    return x ** 3

exponent = {'square': findSquare, 'cube': findCube}

print(exponent['square'](3))    # 9
print(exponent['cube'](3))      # 27

Python Function Executes at Runtime

Because Python treats def as an executable statement, it can appear anywhere a normal statement can.

For example you can nest a function inside an if statement to select between alternative definitions.

Example:

if True:
    def hello():
        print('Hello, World!')
else:
    def hello():
        print('Hello, Universe!')

hello()     # Hello, World!