Skip to content

Defining Functions in Python using the def Keyword

Updated: at 02:12 AM

Functions are one of the core building blocks of programming in Python. They allow you to encapsulate reusable pieces of code under a name, which can be called and executed whenever needed. Defining functions using the def keyword is the standard way to create reusable, modular code in Python.

In this comprehensive guide, we will cover everything you need to know about defining functions in Python, including:

Table of Contents

Open Table of Contents

What are Functions?

A function is a reusable block of code that can be called by name to perform a specific task. Functions allow you to decompose complex problems into smaller, modular pieces that are easier to build, test, and maintain.

Here are some key advantages of using functions:

In Python, you define functions using the def keyword, followed by a function name, parameters in parentheses, and a colon. The code to execute goes in an indented block below.

# Syntax for defining functions

def function_name(parameters):

Let’s look at a simple example:

def say_hello():

This defines a function called say_hello that prints “Hello!” when called. We can call the function by name:

say_hello() # Prints "Hello!"

Functions encapsulate code you wish to reuse. By defining a function once, you can call it as many times as needed without rewriting the print statement each time. This makes code more organized and maintainable.

Function Parameters and Arguments

Functions can also take input data through parameters and arguments:

For example:

# 'name' is a parameter
def print_name(name):

# 'Alice' is the argument

name is a parameter that acts as a variable containing the input to the function. When we call print_name('Alice'), the argument 'Alice' gets assigned to the name parameter during execution.

You can define functions with any number of parameters by separating them with commas:

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

print_fullname('Alice', 'Smith')

The number and order of arguments passed must match the parameters when calling the function, otherwise you will get an error.

You can also set default values for parameters that will be used if no argument is passed for that parameter:

def print_fullname(first, last='Doe'):
  print(f"{first} {last}")

print_fullname('Alice') # Alice Doe
print_fullname('Alice', 'Smith') # Alice Smith

Setting useful defaults makes functions more flexible and reusable.

Return Values from Functions

Functions can process data and return a result back to the caller. This is done using the return statement:

def multiply(num1, num2):
  result = num1 * num2
  return result

product = multiply(2, 3)
# product variable now contains 6

The return statement exits the function and sends the result back to the calling context, where it can be assigned to a variable or used in an expression.

You can return any Python object like numbers, strings, lists, dicts, Booleans, etc. Returning None is the same as having no return statement.

Functions without a return statement implicitly return None. This can be useful when your function performs some task but no return value is needed.

Docstrings for Documentation

Python has a handy feature called docstrings that lets you document your functions by adding a multiline string literal right after the function definition:

def multiply(num1, num2):
  Multiply two numbers and return the result.

    num1 (int or float): The first number
    num2 (int or float): The second number

    int or float: The product of the two numbers

  return num1 * num2

Docstrings provide helpful usage information and are the first place Python looks when invoking help(). Well documented functions serve as their own documentation:


# Output:
Help on function multiply in module __main__:

multiply(num1, num2)
  Multiply two numbers and return the result.

    num1 (int or float): The first number
    num2 (int or float): The second number

    int or float: The product of the two numbers

Docstrings are delimited using triple quotes. The first line should be a short, concise summary of the function’s purpose.

Scope and Namespace of Functions

Functions create a new scope when defined. This scope contains the function’s internal namespace for variables that are local to that function.

Variables defined inside a function only exist within the function’s local scope:

def foo():
  y = 5    # y is a local variable

  print(y) # Prints 5


print(y) # Error, y is not defined

The parameter variables also only exist in the function’s local namespace:

def bar(x):


print(x) # Error, parameter x doesn't exist outside the function

This scoping isolates the function and avoids conflicting with variables in the global scope. Any variables assigned inside the function remain local and are not accessible from outside.

Global variables can be read from a function. But to modify them, you must declare them as global:

count = 0 # global variable

def update_count():
  global count

  count += 1 # Modifying global count

print(count) # Prints 1

In general, functions should access enclosing scopes using parameters and returns rather than global variables.

Nested Functions and Closures

Python allows defining functions inside other functions, known as nested functions.

The nested function can access variables defined in the outer function, creating a closure:

def outer_func():
  x = 'hello'

  def inner_func():

  return inner_func

my_func = outer_func()
my_func() # Prints 'hello'

Here inner_func() is nested inside outer_func(). When outer_func() runs, inner_func() captures the variable x in the enclosing scope and retains access to it. This is called a closure.

Closures let you associate data with your nested functions even after the outer function has finished execution. The inner function remembers the environment in which it was created.

Recursive Functions

Python also allows functions to call themselves, known as recursion.

This is useful for tasks that can be defined recursively like computing factorials, traversing trees, etc:

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

print(factorial(5)) # 120

factorial() calls itself to compute the output. The recursion continues until the base case is reached.

Recursive functions must have a termination condition to avoid infinite loops. The parameters and return value are used to reduce the problem until the base case is triggered.

Lambda Functions

Python has an alternate, inline syntax for defining simple, anonymous functions using the lambda keyword:

multiply = lambda x, y: x * y

print(multiply(3, 5)) # 15

These are called lambda functions or anonymous functions. They are restricted to a single expression. Useful for throwaway functions or passing simple functions as arguments.

Lambda functions can be assigned to variables to give them a name:

full_name = lambda first, last: f"{first} {last}"

print(full_name('Alice', 'Smith')) # Alice Smith

The function is defined and assigned in one line without a def statement. Perfect for minimal, inline functions.

Common Mistakes and Best Practices

Here are some best practices to keep in mind when defining functions:

Some common mistakes to avoid:

Following best practices will make your functions easier to understand, test, and maintain.


Defining reusable functions using def is a fundamental skill in Python. Functions encapsulate logic, abstract away complexity, and help organize your code.

In this guide we covered parameters, arguments, return values, scope, recursion, and more. Feel free to refer back to the code examples and explanations as a technical reference for defining functions in Python.

Mastering functions will enable you to start decomposing larger programs into logical, manageable pieces and design systems in a more modular way. Functions are the building blocks of code reuse and abstraction.

I hope you found this guide helpful! Let me know if you have any other questions as you start defining functions in your own code.