Fonctions paramétrées

Paramètres de fonction

On sait définir et appeler une fonction, mais on obtient toujours la même chose à chaque appel. Il serait bien de pouvoir faire varier le comportement d’une fonction suivant les valeurs de certaines expressions, et c’est là qu’interviennent les paramètres.

Le paramètre est une variable définie dans la fonction qui recevra une valeur lors de chaque appel. Cette valeur pourra être de tout type, suivant ce qui est fourni en argument.
Les noms des paramètres sont inscrits lors de la définition de la fonction, entre les parenthèses qui suivent son nom.

def table_multiplication(n):
    for i in range(1, 11):
        print(n, '×', i, '=', n*i)

Encore une fois, les paramètres sont des variables et donc suivent les mêmes règles de nomenclature. S’il y a plusieurs paramètres, ils doivent être séparés par des virgules.

def hello(firstname, lastname):
    print('Hello', firstname, lastname, '!')

Lors de l’appel de la fonction, on utilisera les arguments pour donner leurs valeurs aux paramètres. On précise ainsi les valeurs dans les parenthèses qui suivent le nom de la fonction.

>>> table_multiplication(3)
3 × 1 = 3
3 × 2 = 6
3 × 3 = 9
3 × 4 = 12
3 × 5 = 15
3 × 6 = 18
3 × 7 = 21
3 × 8 = 24
3 × 9 = 27
3 × 10 = 30
>>> table_multiplication(5)
5 × 1 = 5
5 × 2 = 10
5 × 3 = 15
5 × 4 = 20
5 × 5 = 25
5 × 6 = 30
5 × 7 = 35
5 × 8 = 40
5 × 9 = 45
5 × 10 = 50

Le comportement est le même pour les fonctions à plusieurs paramètres, les valeurs leur sont attribuées dans l’ordre des arguments : le premier paramètre prend la valeur du premier argument, etc.
Chaque argument correspond ainsi à un paramètre (et inversement).

>>> hello('Père', 'Noël')
Hello Père Noël !
>>> hello('Blanche', 'Neige')
Hello Blanche Neige !

Une erreur survient s’il n’y a pas assez d’arguments pour compléter tous les paramètres.

>>> hello()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: hello() missing 2 required positional arguments: 'firstname' and 'lastname'
>>> hello('Asterix')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: hello() missing 1 required positional argument: 'lastname'

Au contraire, une autre erreur est levée s’il y a trop d’arguments par rapport au nombre de paramètres.

>>> hello('Homer', 'Jay', 'Simpson')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: hello() takes 2 positional arguments but 3 were given

Derrière ces exemples simples, il est bien sûr possible d’avoir de vrais comportements qui dépendent des valeurs de nos paramètres.

def print_div(a, b):
    if b == 0:
        print('Division impossible')
    else:
        print(a / b)
>>> print_div(5, 2)
2.5
>>> print_div(1, 0)
Division impossible

Espace de noms

Chaque fonction comporte son propre espace de noms (ou scope), c’est-à-dire une entité qui contient toutes les définitions de variables. Ainsi, une variable définie à l’intérieur d’une fonction n’existe que dans celle-ci.

>>> def f():
...     a = 5
...     print(a)
...
>>> a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined

Que ce soit avant ou après l’appel de la fonction, la variable a n’existe pas dans l’espace de noms principal (ou global).

>>> f()
5
>>> a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined

Il en est de même pour les paramètres qui sont au final des variables comme les autres au sein de la fonction.

>>> def f(x):
...     print(x)
... 
>>> f(5)
5
>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

Il est en revanche possible pour une fonction d’accéder aux variables définies à l’extérieur de celle-ci.

>>> value = 42
>>> 
>>> def f():
...     print(value)
... 
>>> f()
42

Ce qui implique que le comportement de la fonction change si la valeur de la variable est modifiée.

>>> value = 13
>>> f()
13

Mais les espaces de noms extérieur et intérieur à la fonction sont bien deux scopes distincts. Deux variables de même nom peuvent exister dans des scopes différents sans qu’elles n’interfèrent entre elles.

>>> x = 0
>>> 
>>> def f():
...     x = 1
...     print(x)
... 
>>> x
0
>>> f()
1
>>> x
0

Ce qui implique qu’il n’est pas possible de redéfinir une variable extérieure à la fonction (du moins pas de cette manière) car Python croira toujours que l’on cherche à définir une variable locale.

Cela peut poser problème si l’on essaie dans une fonction d’accéder à une variable avant de la redéfinir. En effet, Python ne saura pas si l’on souhaite récupérer une variable extérieure portant ce nom (puisqu’elle n’aura pas encore été définie dans le scope local) ou en définir une nouvelle. Il lèvera donc une erreur pour éviter toute ambigüité.

>>> def f():
...     print(x)
...     x = 1
... 
>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f
UnboundLocalError: local variable 'x' referenced before assignment

Arguments positionnels et nommés

Nous n’avons vu pour le moment que les arguments positionnels. C’est-à-dire dont l’association avec les paramètres se fait par la position de l’argument (n-ième argument pour le n-ième paramètre).

Mais il est aussi possible de spécifier des arguments nommés, où la correspondance avec le paramètre se fait par le nom.

>>> def f(a, b):
...     print(a, b)
... 
>>> f(a=1, b=2)
1 2

Dans ce contexte, il est possible d’inverser l’ordre des paramètres (puisqu’il n’importe pas pour les identifier).

>>> f(b=2, a=1)
1 2

Il est possible de passer à la fois des arguments positionnels et nommés, mais les positionnels devront toujours se trouver avant (puisque c’est leur position qui détermine le paramètre).

>>> f(1, b=2)
1 2
>>> f(b=2, 1)
  File "<stdin>", line 1
SyntaxError: positional argument follows keyword argument

Un paramètre ne peut toujours correspondre qu’à un seul argument, Python lèvera donc une erreur s’il reçoit deux arguments (un positionnel et un nommé) pour un même paramètre.

>>> f(1, 2, b=3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() got multiple values for argument 'b'

Ou si un argument nommé est répété.

>>> f(1, b=2, b=3)
  File "<stdin>", line 1
SyntaxError: keyword argument repeated

De la même manière, il n’est pas possible de préciser un argument positionnel pour le second paramètre sans en préciser pour le premier (puisque le premier argument positionnel est forcément destiné au premier paramètre).
Ainsi, dans l’appel suivant Python considèrera qu’il reçoit l’argument positionnel 2 pour le paramètre a (le premier) et donc lèvera aussi une erreur.

>>> f(2, a=1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() got multiple values for argument 'a'