Functions in a programming language

Reading time10 min

En bref

Résumé de l’article

Dans cet article, nous introduisons la notion de fonctions. C’est un concept clé en programmation. Nous montrons comment déclarer des fonctions, et comment les utiliser.

Points clés

  • Une fonction permet de regrouper des blocs de code pour une meilleure réutilisation et une meilleure structuration du code.

  • Elle doit être déclarée pour être utilisée.

  • Le nom, les arguments et le type de retour d’une fonction forment sa signature.

  • Appeler une fonction associe des valeurs aux paramètres déclarés.

  • Attention aux paramètres mutables !

  • N’oubliez pas de documenter vos fonctions.

Contenu de l’article

Définir et utiliser des fonctions sont des concepts clés en programmation pour assurer la lisibilité et la réutilisabilité de votre code. Les fonctions permettent de :

  • Factoriser (regrouper) des instructions qui effectuent une tâche spécifique.

  • Être appelées à la demande n’importe où dans votre code.

  • Être testées indépendamment pour ensuite être réutilisées et partagées en toute sécurité.

1 — Déclarer une fonction

Pour être utilisée dans un code, une fonction doit d’abord être déclarée.

En Python, une fonction est déclarée en utilisant le mot-clé def, suivi du nom de la fonction et de parenthèses contenant les paramètres éventuels. Selon la norme PEP 8 (voir la session 1 pour un rappel), les noms de fonctions doivent être en minuscules, avec les mots séparés par des underscores.

Une fonction peut prendre des paramètres et retourner une valeur. Si aucune valeur n’est retournée, on parle généralement de “procédure”, et non de fonction.

Le nom d’une fonction, le nombre et les types de ses arguments, ainsi que le type de sa ou ses valeurs retournées, forment ce qu’on appelle généralement la “signature” ou le “prototype” d’une fonction.

Exemple

Voici la définition d’une fonction en Python nommée func_name qui attend deux paramètres formels, et retourne une valeur.

Si on le souhaite, on peut indiquer le type des arguments et de la valeur retournée (voir la session 1 pour un rappel). Indiquons que la fonction attend deux entiers, et produit une valeur réelle.

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) {

    }

}

Il est aussi possible de déclarer des valeurs par défaut pour certains ou tous les arguments. Pour cela, vous pouvez les indiquer lors de la déclaration.

Exemple

Ajoutons des valeurs par défaut aux deux paramètres :

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) {

    }

}

Il est possible d’avoir un mélange de paramètres initialisés et non initialisés. Pour cela, les paramètres sans valeur par défaut doivent être déclarés en premier.

2 — Appeler une fonction

Une fois déclarée, une fonction peut être appelée depuis n’importe où dans votre code, tant que Python peut la trouver. Nous parlerons de cet aspect dans le cours sur l’organisation du code.

Pour appeler une fonction, il suffit d’utiliser son nom et de fournir une valeur pour chaque paramètre attendu. Les valeurs données lors d’un appel de fonction sont appelées “paramètres effectifs” ou “arguments”.

Exemple

Reprenons cet exemple. Voici comment appeler func_name avec deux arguments. La valeur 4 est associée au paramètre param_1, tandis que la valeur 2 est associée à param_2. Le résultat de la fonction est stocké dans une variable appelée 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);
    }

}

Voici quelques remarques importantes :

  • Les arguments doivent être passés dans le même ordre que dans la déclaration de la fonction. En effet, si on inverse 4 et 2 dans l’exemple ci-dessus, cela échangera les valeurs de param_1 et param_2 dans func_name.

  • Les arguments avec des valeurs par défaut n’ont pas besoin d’être spécifiés.

    Exemple

    Par exemple, dans l’exemple ci-dessus, si nous voulons que param_2 soit égal à 456 dans func_name, les deux codes suivants seraient corrects :

    # 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);
        }
    
    }
  • Il est possible d’appeler une fonction avec des “arguments nommés”. Pour cela, vous pouvez indiquer le nom du paramètre déclaré avant de lui attribuer une valeur lors de l’appel de la fonction. Cela peut être très pratique en combinaison avec des valeurs par défaut, pour ne modifier qu’un paramètre particulier.

    Exemple

    Par exemple, si nous voulons que param_2 soit fixé à 0 tandis que param_1 garde sa valeur par défaut, nous pouvons faire comme suit. Notez que ce n’est pas directement faisable en Java.

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

3 — Arguments passés par copie ou par référence

Lorsque nous appelons une fonction, un lien est fait entre la valeur passée en argument, et le nom du paramètre qui la reçoit. Cependant, les types de données complexes tels que les listes, dictionnaires, etc. peuvent être assez volumineux en mémoire. Pour cette raison, tous les types en Python sont passés par “référence”.

En d’autres termes, ils ne sont pas copiés lorsqu’ils sont passés à la fonction, mais c’est l’adresse mémoire où ils sont stockés qui est donnée à la fonction. Cela coûte beaucoup moins en termes de calculs nécessaires.

Cependant, cela comporte un risque majeur, pour les types mutables. En effet, puisque c’est l’adresse en mémoire qui est passée et non une copie de l’élément, vous pouvez modifier une variable en pensant travailler sur une copie locale.

Exemple

Ici, une fonction reçoit une liste l et retire un élément de celle-ci. Si vous considérez l comme une variable locale de example, alors elle devrait être la même une fois que example se termine. Cependant ce n’est pas le cas.

# 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);
    }

}

Voici la sortie que nous obtenons :

Sortie
[1, 2, 3]
[1, 2]
[1, 2, 3]
[1, 2]

Donc, attention aux arguments passés par référence !

4 — Typage et documentation des fonctions

La motivation principale pour l’utilisation des fonctions est de faciliter la réutilisation du code. Pour utiliser une fonction, vous n’avez pas nécessairement besoin de comprendre son contenu, c’est-à-dire l’algorithme sous-jacent. Vous avez seulement besoin de connaître son nom, ses paramètres et le type de valeur qu’elle retourne. D’où l’importance de bien prototyper et documenter vos fonctions.

Le typage par annotations fournit un moyen d’ajouter des indications de type dans votre code. Même si ces indications sont seulement informatives et ne sont pas prises en compte par l’interpréteur Python, elles facilitent indéniablement la réutilisation des fonctions. Ces indications doivent préciser le type de chaque paramètre et le type de la valeur retournée.

De plus, la première ligne de votre fonction devrait être une docstring contenant le manuel utilisateur. Cette documentation devrait (au minimum) décrire ce que fait la fonction, ses entrées et sorties.

Voici deux versions d’une même fonction, laquelle seriez-vous plus enclin à utiliser ?

# 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 vals 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;
    }

}

Pour aller plus loin

5 — Fonctions avec un nombre variable de paramètres

Une fonction peut accepter un nombre illimité de paramètres. Pour cela, il est possible de déclarer une fonction avec des paramètres particuliers :

  • *args – Avoir ce paramètre à la fin d’une fonction permet d’accepter n’importe quel paramètre.
  • **kwargs – Avoir ce paramètre à la fin d’une fonction permet d’accepter n’importe quel paramètre nommé.
Exemple

Voici un exemple de procédure qui prend un nombre variable de paramètres et de paramètres nommés, et les affiche tous :

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));
    }

}

Voici la sortie que nous obtenons :

Sortie
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

Il est possible qu’une fonction ait des paramètres standards, des paramètres nommés, et des paramètres variables supplémentaires, le tout en même temps. Pour cela, vous devez les déclarer dans cet ordre spécifique.

6 — Fonctions lambda

Une “fonction lambda” est une fonction qui n’a pas de nom. Typiquement, le code suivant est une fonction qui prend une entrée x et retourne true si x est pair, false sinon :

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

Parfois, on veut juste définir une fonction pour un usage unique, sans avoir à la nommer. Cela peut être utile, notamment lorsqu’une fonction prend une autre fonction en entrée.

Par exemple, en Python, la fonction map applique une fonction donnée à tous les éléments d’une liste.

# 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);
    }

}

Pour aller plus loin

  • Lambda calculus.

    L’article Wikipédia sur les fonctions lambda et le calcul lambda.