Functions in a programming language

Reading time10 min

In brief

Article summary

In this article, we introduce the notion of functions. This is a key concept in programming. We show how to declare functions, and how to use them.

Main takeaways

  • A function allows to group some blocks of codes for better reuse, and better code structuration.

  • It needs to be declared to be used.

  • The name, arguments and return type of a function form its signature.

  • Calling a function maps values to declared parameters.

  • Beware of mutable parameters!

  • Do not forget to document your functions.

Article contents

Defining and using functions are key programming concepts to ensure the readability and reusability of your code. Functions allow to:

  • Factorize (regroup) statements that perform a specific task.

  • Be called on demand anywhere in your code.

  • Be tested independently to then be safely reused and shared.

1 — Declaring a function

To be used in a code, a function first needs to be declared.

In Python, a function is declared using the def keyword, followed by the function name and parentheses containing any parameters. According to the PEP 8 norm (see session 1 for a reminder), function names should be lowercase, with words separated by underscores.

A function can take parameters and return a value. If no value is returned, it is generally called a “procedure”, not a function.

The name of a function, the number and types of its arguments, and the type of its returned value(s) is generally called the “signature”, or the “prototype” of a function.

Example

Here is the definition of a function in Python named func_name expecting two formal parameters, and returning a value.

If we want, we can hint the type of the arguments and returned value (see session 1 for a reminder). Let’s indicate that the function expects two integers, and produces a real value.

def func_name (param_1: int, param_2: int) -> float:

    """
        This is an example of how to define a function in Python.
        Here, we use type hinting to indicate to the user how they should use it. 
        In:
            * param_1: The first argument to take as an input.
            * param_2: The second argument to take as an input.
        Out:
            * A result computed by the function.
    """

    # Do something
    ...

    # Return something
    return result
/**
 * To run this code, you need to have Java installed on your computer, then:
 * - Create a file named `Main.java` in a directory of your choice.
 * - Copy this code in the file.
 * - Open a terminal in the directory where the file is located.
 * - Run the command `javac Main.java` to compile the code.
 * - Run the command `java -ea Main` to execute the compiled code.
 * Note: '-ea' is an option to enable assertions in Java.
 */
public class Main {

    /**
     * This is the function we want to define.
     * Note that in Java, we need to specify the types of the variables (here, integers) and returned value (here, a float).
     * Contrary to Python, this is needed for compilation, not just hinting.
     * You can ignore the "static" keyword for now, you will learn about it later in your studies.
     *
     * @param param1 The first argument to take as an input.
     * @param param2 The second argument to take as an input.
     * @return A result computed by the function.
     */
    public static float funcName(int param1, int param2) {
        // Do something
        // ...

        // Return something
        return result;
    }


    /**
     * This is the entry point of your program.
     * It contains the first codes that are going to be executed.
     *
     * @param args Command line arguments received.
     */
    public static void main(String[] args) {

    }

}

It is also possible to declare default values for some or all of the arguments. To do so, you can indicate it during the declaration.

Example

Let’s add some default values to both parameters:

def func_name (param_1: int = 123, param_2: int = 456) -> float:

    """
        This is an example of how to define a function in Python.
        Here, we use type hinting to indicate to the user how they should use it. 
        In:
            * param_1: The first argument to take as an input.
            * param_2: The second argument to take as an input.
        Out:
            * A result computed by the function.
    """

    # Do something
    ...

    # Return something
    return result
/**
 * To run this code, you need to have Java installed on your computer, then:
 * - Create a file named `Main.java` in a directory of your choice.
 * - Copy this code in the file.
 * - Open a terminal in the directory where the file is located.
 * - Run the command `javac Main.java` to compile the code.
 * - Run the command `java -ea Main` to execute the compiled code.
 * Note: '-ea' is an option to enable assertions in Java.
 */
public class Main {

    /**
     * This is the function we want to define.
     * Note that in Java, we need to specify the types of the variables (here, integers) and returned value (here, a float).
     * Contrary to Python, this is needed for compilation, not just hinting.
     * You can ignore the "static" keyword for now, you will learn about it later in your studies.
     *
     * @param optionalParams Possible int parameters that are optional.
     * @return A result computed by the function.
     */
    public static float funcName(int... optionalParams) {
        // Set default value
        int param1 = optionalParams.length > 0 ? optionalParams[0] : 123;
        int param2 = optionalParams.length > 1 ? optionalParams[1] : 456;

        // Do something
        // ...

        // Return something
        return result;
    }


    /**
     * This is the entry point of your program.
     * It contains the first codes that are going to be executed.
     *
     * @param args Command line arguments received.
     */
    public static void main(String[] args) {

    }

}

It is possible to have a mix of initialized parameters and non-initialized ones. To do so, parameters with no default value should be declared first.

2 — Calling a function

Once declared, a function can be called from anywhere in your code, as long as Python can find it. We will talk about this aspect in the course on code organization.

To call a function, just use its name and a value for each expected parameter. Values given in a function call are called “actual parameters” or “arguments”.

Example

Let’s work on this example again. Here is how to call func_name with two arguments. Value 4 is mapped to parameter param_1, while value 2 is mapped to param_2. The output of the function is stored on a variable called res.

def func_name (param_1: int = 123, param_2: int = 456) -> float:

    """
        This is an example of how to define a function in Python.
        Here, we use type hinting to indicate to the user how they should use it. 
        In:
            * param_1: The first argument to take as an input.
            * param_2: The second argument to take as an input.
        Out:
            * A result computed by the function.
    """

    # Do something
    ...

    # Return something
    return result



# Call the function
res = func_name(4, 2)
/**
 * To run this code, you need to have Java installed on your computer, then:
 * - Create a file named `Main.java` in a directory of your choice.
 * - Copy this code in the file.
 * - Open a terminal in the directory where the file is located.
 * - Run the command `javac Main.java` to compile the code.
 * - Run the command `java -ea Main` to execute the compiled code.
 * Note: '-ea' is an option to enable assertions in Java.
 */
public class Main {

    /**
     * This is the function we want to define.
     * Note that in Java, we need to specify the types of the variables (here, integers) and returned value (here, a float).
     * Contrary to Python, this is needed for compilation, not just hinting.
     * You can ignore the "static" keyword for now, you will learn about it later in your studies.
     *
     * @param optionalParams Possible int parameters that are optional.
     * @return               A result computed by the function.
     */
    public static float funcName(int... optionalParams) {
        // Set default value
        int param1 = optionalParams.length > 0 ? optionalParams[0] : 123;
        int param2 = optionalParams.length > 1 ? optionalParams[1] : 456;

        // Do something
        ...

        // Return something
        return result;
    }

    /**
     * This is the entry point of your program.
     * It contains the first codes that are going to be executed.
     *
     * @param args Command line arguments received.
     */
    public static void main(String[] args) {
        // Call the function
        float res = funcName(4, 2);
    }

}

Here are a few important remarks:

  • Arguments should be passed in the same order as in function declaration. Indeed, if we invert 4 and 2 in the example above, it will swap the values of param_1 and param_2 in func_name.

  • Arguments with default values do not need to be specified.

    Example

    For instance, in the example above, if we wanted to have param_2 equal to 456 in func_name, both of the following codes would be correct:

    # Call the function
    res = func_name(4, 456)
    res = func_name(4)
    /**
     * To run this code, you need to have Java installed on your computer, then:
     * - Create a file named `Main.java` in a directory of your choice.
     * - Copy this code in the file.
     * - Open a terminal in the directory where the file is located.
     * - Run the command `javac Main.java` to compile the code.
     * - Run the command `java -ea Main` to execute the compiled code.
     * Note: '-ea' is an option to enable assertions in Java.
     */
    public class Main {
    
        /**
         * This is the entry point of your program.
         * It contains the first codes that are going to be executed.
         *
         * @param args Command line arguments received.
         */
        public static void main(String[] args) {
            // Call the function
            float res = funcName(4, 456);
            res = funcName(4);
        }
    
    }
  • It is possible to call a function with “named arguments”. To do so, you can indicate the declared parameter name before setting its value during the function call. This can be very practical in combination with default values, to only set a particular parameter.

    Example

    For instance, if we want param_2 to be set to 0 while param_1 keeps its default value, we can do as follows. Note that it is not directly feasible in Java.

    # Call the function
    res = func_name(param_2=0)

3 — Arguments passed by copy or reference

When we call a function, a mapping is made between the value passed as argument, and the name of the parameter received it. However, complex data types such as lists, dictionaries, etc. can be quite large in memory. For this reason, all types in Python are passed by “reference”.

In other words, they are not copied when passed to the function, but instead the memory addressed where they are stored is given to the function. This is a lot less costly in terms of needed computations.

However, it has a major risk associated to it, for mutable types. Indeed, since the address in memory is passed and not a copy of the element, you may modify a variable while thinking you work on a local copy.

Example

Here, a function receives a list l and pops an element from it. If you think of l as a local variable of example, then it should be the same once example terminates. However this is not the case.

# Needed imports
from typing import List



def example (l: List[int]) -> None:

    """
        This is an example a function that manipulates a list.
        It will modify the list globally and not locally.
        In:
            * l: The list to work on.
        Out:
            * None.
    """

    # Pop an element from l
    e = l.pop()



# Call the function
my_list = [1, 2, 3]
print(my_list)
example(my_list)
print(my_list)
// Needed variables
import java.util.List;
import java.util.ArrayList;

/**
 * To run this code, you need to have Java installed on your computer, then:
 * - Create a file named `Main.java` in a directory of your choice.
 * - Copy this code in the file.
 * - Open a terminal in the directory where the file is located.
 * - Run the command `javac Main.java` to compile the code.
 * - Run the command `java -ea Main` to execute the compiled code.
 * Note: '-ea' is an option to enable assertions in Java.
 */
public class Main {

    /**
     * This is an example of a method that manipulates a list.
     * It will modify the list globally and not locally.
     *
     * @param l The list to work on.
     */
    public static void example(List<Integer> l) {
        // Remove the last element from the list
        if (!l.isEmpty()) {
            int e = l.remove(l.size() - 1);
        }
    }

    /**
     * This is the entry point of your program.
     * It contains the first codes that are going to be executed.
     *
     * @param args Command line arguments received.
     */
    public static void main(String[] args) {
        // Initialize the list
        List<Integer> myList = new ArrayList<>();
        myList.add(1);
        myList.add(2);
        myList.add(3);

        // Call the function
        System.out.println(myList);
        example(myList);
        System.out.println(myList);
    }

}

Here is the output we get:

Output
[1, 2, 3]
[1, 2]
[1, 2, 3]
[1, 2]

So, beware of arguments passed by reference!

4 — Typing and documenting functions

The main motivation for the use of functions is to ease the reuse of code. To use a function, you do not necessarily need to understand its content, i.e., the underlying algorithm. You only need to know its name, its parameters and the type of value it returns. Hence, the importance of clearly prototyping and documenting your functions.

Type hinting provides a way to add typing hints in your code. Even if these hints are informative only and not taken into account by the Python interpreter, they undeniably ease the reuse of functions. Such hints have to indicate the type of each parameter and the type of the returned value.

Moreover, the first line of your function should be a docstring containing the user manual. This documentation should (at least) describe what the function does, its inputs and outputs.

Here are two versions of a same function, which one would you be more willing to use?

# Needed imports
from typing import List



def divs (x, w):

    return [x2 for x2 in x if not x2 % w]
# Needed imports
from typing import List



def get_dividers (vals: List[int], w: int) -> List[int]:

    """
        Returns a list of values from vals that are multiple of w.
        In:
            * vals: The values to test.
            * w     The divider.
        Out:
            * The values in vals that are multiples of w.
    """

    return [x2 for x2 in x if not x2 % w]
// Needed imports
import java.util.List;
import java.util.ArrayList;



public class MyFunctions {

    public List<Integer> divs(List<Integer> x, int w) {
        List<Integer> result = new ArrayList<>();
        for (int x2 : x) {
            if (x2 % w == 0) {
                result.add(x2);
            }
        }
        return result;
    }
    
}
// Needed imports
import java.util.List;
import java.util.ArrayList;

/**
 * This class contains some example functions to illustrate documentation.
 */
public class MyFunctions {

    /**
     * Returns a list of values from vals that are multiple of w.
     * 
     * @param vals The values to test.
     * @param w    The divider.
     * @return     The values in vals that are multiples of w.
     */
    public List<Integer> getDividers(List<Integer> vals, int w) {
        List<Integer> result = new ArrayList<>();
        for (int v : vals) {
            if (v % w == 0) {
                result.add(v);
            }
        }
        return result;
    }

}

To go further

Important

The content of this section is optional. It contains additional material for you to consolidate your understanding of the current topic.

5 — Functions with a variable number of parameters

A function may accept an unlimited number of parameters. To do so, it is possible to declare a function with particular parameters:

  • *args – Having this parameter at the end of a function allows to accept any parameter.
  • **kwargs – Having this parameter at the end of a function allows to accept any named parameter.
Example

Here is an example of a procedure that takes a variable number of parameters and named parameters, and prints them all:

def print_args (*args, **kwargs) -> None:

    """
        This is an example of how to define a variable number of arguments in Python.
        In:
            * args:   The unnamed arguments.
            * kwargs: The named arguments.
        Out:
            * None.
    """

    # Print arguments
    for param in args:
        print(param)
    
    # Print named arguments
    for param_name in kwargs:
        print(param_name, "=", kwargs[param_name])



# Call the function with some distinct sets of arguments
print_args(1, 2, 3)
print("=" * 10)
print_args(one_named_arg=123)
print("=" * 10)
print_args(1, 2, 3, fourth=4, fifth=5)
print("=" * 10)
print_args("Hello", "this is a test", one_named_arg=123)
// Needed imports
import java.util.Map;

/**
 * To run this code, you need to have Java installed on your computer, then:
 * - Create a file named `Main.java` in a directory of your choice.
 * - Copy this code in the file.
 * - Open a terminal in the directory where the file is located.
 * - Run the command `javac Main.java` to compile the code.
 * - Run the command `java -ea Main` to execute the compiled code.
 * Note: '-ea' is an option to enable assertions in Java.
 */
public class Main {

    /**
     * This is an example of how to define a variable number of arguments in Java.
     *
     * @param args   The unnamed arguments.
     * @param kwargs The named arguments.
     */
    public static void printArgs(Object[] args, Map<String, Object> kwargs) {
        // Print arguments
        for (Object param : args) {
            System.out.println(param);
        }

        // Print named arguments
        for (Map.Entry<String, Object> entry : kwargs.entrySet()) {
            System.out.println(entry.getKey() + " = " + entry.getValue());
        }
    }

    /**
     * This is the entry point of your program.
     * It contains the first codes that are going to be executed.
     *
     * @param args Command line arguments received.
     */
    public static void main(String[] args) {
        // Call the function with some distinct sets of arguments
        printArgs(new Object[]{1, 2, 3}, Map.of());
        System.out.println("==========");
        printArgs(new Object[]{}, Map.of("one_named_arg", 123));
        System.out.println("==========");
        printArgs(new Object[]{1, 2, 3}, Map.of("fourth", 4, "fifth", 5));
        System.out.println("==========");
        printArgs(new Object[]{"Hello", "this is a test"}, Map.of("one_named_arg", 123));
    }

}

Here is the output we get:

Output
1
2
3
==========
one_named_arg = 123
==========
1
2
3
fourth = 4
fifth = 5
==========
Hello
this is a test
one_named_arg = 123
1
2
3
==========
one_named_arg = 123
==========
1
2
3
fifth = 5
fourth = 4
==========
Hello
this is a test
one_named_arg = 123

It is possible for a function to have some standard parameters, some named parameters, and some extra variable parameters, all at once. To do so, you have to declare them in that specific order.

6 — Lambda functions

A “lambda function” is a function that has no name. Typically, the following code is a function that takes an input x and returns true if x is even, false otherwise:

lambda x : x % 2 == 0
x -> x % 2 == 0

Sometimes, we just want to define a function for a single usage, without having to name it. This can be useful, especially when a function takes another function as an input.

For instance, in Python, the map function applies a given function to all elements of a list.

# Use map to indicate if a number is even
l = list(range(100))
print("Even numbers:", list(map(lambda x : x % 2 == 0, l)))
// Needed imports
import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;

/**
 * To run this code, you need to have Java installed on your computer, then:
 * - Create a file named `Main.java` in a directory of your choice.
 * - Copy this code in the file.
 * - Open a terminal in the directory where the file is located.
 * - Run the command `javac Main.java` to compile the code.
 * - Run the command `java -ea Main` to execute the compiled code.
 * Note: '-ea' is an option to enable assertions in Java.
 */
public class Main {

    /**
     * This is the entry point of your program.
     * It contains the first codes that are going to be executed.
     *
     * @param args Command line arguments received.
     */
    public static void main(String[] args) {
        // Initialize the list
        List<Integer> l = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            l.add(i);
        }

        // Use map to indicate if a number is even
        List<Boolean> evenNumbers = l.stream().map(x -> x % 2 == 0).collect(Collectors.toList());
        System.out.println("Even numbers: " + evenNumbers);
    }

}

To go beyond

Important

The content of this section is very optional. We suggest you directions to explore if you wish to go deeper in the current topic.

  • Lambda calculus.

    The Wikipedia article on lambda functions and lambda calculus.