Functions in a programming language
Reading time10 minEn 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.
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.
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”.
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
et2
dans l’exemple ci-dessus, cela échangera les valeurs deparam_1
etparam_2
dansfunc_name
. -
Les arguments avec des valeurs par défaut n’ont pas besoin d’être spécifiés.
ExemplePar exemple, dans l’exemple ci-dessus, si nous voulons que
param_2
soit égal à456
dansfunc_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.
ExemplePar exemple, si nous voulons que
param_2
soit fixé à0
tandis queparam_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.
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 :
[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é.
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 :
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.