Skip to content

Creating and Documenting Custom Functions in Python

Updated: at 04:45 AM

Python allows developers to create reusable and modular code by defining custom functions. Functions are one of the core building blocks of Python programming. They help break large programs down into smaller, more manageable pieces that can be called whenever needed. In this guide, we will learn how to define custom functions in Python, provide parameters and return values, include documentation, raise exceptions, and use best practices when creating functions.

Table of Contents

Open Table of Contents

Function Basics

A function in Python is defined using the def keyword, followed by the function name and parameters in parentheses. The code to be executed is indented in the next lines. Here is the basic syntax:

def function_name(param1, param2):
    """Function documentation string"""
    # Function body
    return value

To call the function, simply use the function name with required parameters:

result = function_name(arg1, arg2)

Let’s see an example function definition that adds two numbers:

def add(num1, num2):
    """Adds two numbers together."""
    sum = num1 + num2
    return sum

result = add(5, 3)
print(result) # 8

Function Parameters

Parameters allow passing data into a function so it can operate on it. They act as variables that are initialized to the values passed in. Any immutable data types like numbers, strings, tuples can be used as parameters.

Parameters are defined in the function definition by naming each parameter separated by a comma. These named parameters become variables accessible in the function body.

For example, a function to calculate area of a circle requires a radius parameter:

def circle_area(radius):
  pi = 3.14159
  area = pi * radius * radius
  return area

Here we have a single radius parameter that the function uses to calculate the area.

We can also specify default values for parameters so caller can optionally pass them:

def insert_data(name, age=25):
  print(f"Name: {name}, Age: {age}")

insert_data("John") # Uses default age
# Name: John, Age: 25

insert_data("Mary", 30) # Passes age
# Name: Mary, Age: 30

Positional vs Keyword Arguments

When calling a function in Python, arguments can be passed in two ways:

  1. Positional Arguments - Based on position of parameter in function definition.

  2. Keyword Arguments - By specifying parameter name with values.

For example:

def full_name(first, last):
    print(f"{first} {last}")

full_name("John", "Doe") # Positional
full_name(first="John", last="Doe") # Keyword

Keyword arguments provide more explicit and readable function calls. The order also does not matter when using keyword arguments.

Return Values

The return statement is used to return a value from the function back to the caller. This is helpful when we want to reuse the output of a function.

For example:

def multiply(a, b):
  result = a * b
  return result

x = multiply(2, 3)
print(x) # 6

We can return any object like numbers, strings, lists, dictionaries from a function in Python.

Return statements are optional. If no return statement is defined, Python automatically returns None.


Docstrings in Python are string literals that are used to document functions, modules, and classes. They describe what the function does and how to use it.

Docstrings are defined as the first statement after the function definition, before any other code.

Docstring Format

"""Summary line describing function

Extended description explaining the function's purpose, parameters,
return values, side effects, etc.

Any other relevant details.

Let’s add a docstring to our circle_area() function:

def circle_area(radius):
  """Calculates and returns the area of a circle given radius."""
  pi = 3.14159
  area = pi * radius * radius
  return area

We can access the docstring of a function using the __doc__ attribute:


# Calculates and returns the area of a circle given radius.

Including high quality docstrings is considered a best practice in Python as they help document our code.

Default Arguments

We can specify a default value for a parameter by using an assignment in the function definition:

def log(message, type='INFO'):
  print(f"{type}: {message}")

Here if the type parameter is not passed, it takes the default value 'INFO'.

Default values should be used judiciously after careful consideration. Overuse of default arguments can make code difficult to understand.

All parameters with default values must be listed after parameters without default values in the function definition.

Variable Scope

Parameters and variables declared inside a function only exist within the local scope of that function. They cannot be referenced outside the function. For example:

def calculate_sum(x, y):
  total = x + y # Local variable
  return total

print(total) # ERROR, total not defined

The total variable only exists within the calculate_sum() function.

However, a function can access global variables declared outside the function. But it is not a good practice as it can lead to unintended consequences.

Best Practices for Functions

Here are some key best practices to follow when defining Python functions:

Common Built-in Functions

Python provides many in-built functions that are readily available for use. Some commonly used built-in functions include:

These in-built functions implement core functionality that is frequently required in programs. We can simply call them rather than re-implementing similar logic ourselves.

User-defined vs Built-in Functions

User-defined functions are created by developers while built-in functions come packaged with Python.

Some key differences:

In general, use user-defined functions to encapsulate custom program logic and built-ins for generic operations.

Raising Exceptions

Exceptions provide a way to handle errors and anomalous situations in our programs. In Python, exceptions are raised when an error occurs and can be handled using try and except blocks.

We can also manually raise exceptions in our functions using the raise keyword:

def validate_age(age):
  if age < 0:
    raise ValueError("Age cannot be negative")
  if age > 200:
    raise ValueError("Age too large")

validate_age(-10) # Raises ValueError

The raise statement stops function execution and throws the specified exception. This exception can be handled by the caller.

Common built-in exceptions used with raise include:

Manual exception raising makes code cleaner by separating validation logic from regular code. The caller is forced to handle validation failures explicitly rather than relying on return codes or status flags.

Recursive Functions

Python supports recursive functions, which are functions that call themselves during execution. This is useful for algorithms that can be defined recursively like sorting, traversal, etc.

For example, calculating factorial of a number recursively:

def factorial(x):
  if x == 1:
    return 1
    return x * factorial(x-1)

print(factorial(5)) # 120

Anonymous/Lambda Functions

Python supports small anonymous functions that are not bound to a name using the lambda keyword:

double = lambda x: x * 2
print(double(7)) # 14


Defining custom functions is a critical skill in Python for writing reusable code. Functions encapsulate logic, take parameters and return results. Docstrings provide documentation while exceptions handle errors. Following best practices for naming, scope and signatures improves code quality. Built-in functions offer commonly used utilities. Recursive functions and lambdas provide additional capabilities. With this comprehensive guide, you should feel confident authoring custom functions in Python!