Définir des fonctions avec des arguments par défaut en Python

Vous voulez définir une fonction ou une méthode dont un ou plusieurs de ces arguments sont facultatifs et ont une valeur par défaut.

En premier lieu, la définition d’une fonction avec des arguments optionnels est simple : il suffit d’assigner des valeurs dans la définition et de s’assurer que les arguments par défaut apparaissent en dernier. Par exemple:

def mafct(a, b=42):
    print(a, b)

mafct(1)      # Ok. a=1, b=42
mafct(1, 2)   # Ok. a=1, b=2

Si la valeur par défaut est supposée être un conteneur mutable, tel qu’une liste, un ensemble ou un dictionnaire, utilisez None comme valeur par défaut et écrivez du code comme ceci:

# Utilisation d'une liste comme valeur par défaut
def mafct(a, b=None):
    if b is None:
        b = []
    …

Si, au lieu de fournir une valeur par défaut, vous voulez écrire du code qui teste simplement si un argument optionnel a reçu une valeur intéressante ou non, utilisez ce code:

_no_value = object()

def mafct(a, b=_no_value):
    if b is _no_value:
        print('Aucune valeur b fournie')
    ...

Voici comment se comporte cette fonction:

>>> mafct(1)
Aucune valeur b fournie
>>> mafct(1, 2)     # b = 2
>>> mafct(1, None)  # b = None
>>>

Observez attentivement qu’il y a une distinction entre le fait de ne passer aucune valeur du tout et le fait de passer une valeur None.

Définir des fonctions avec des arguments par défaut est facile, mais c’est un peu plus compliqué qu’il n’y paraît.

Tout d’abord, les valeurs affectées par défaut ne sont définies qu’une seule fois au moment de la définition de la fonction. Essayez cet exemple pour le voir:

>>> x = 42
>>> def mafct(a, b=x):
...     print(a, b)
...
>>> mafct(1)
1 42
>>> x = 23     # N'a aucun effet
>>> mafct(1)
1 42
>>>

Notez que la modification de la variable x (qui était utilisée comme valeur par défaut) n’a aucun effet. En effet, la valeur par défaut a été fixée au moment de la définition de la fonction.

Deuxièmement, les valeurs assignées par défaut doivent toujours être des objets immuables, tels que None, True, False, nombres, ou chaînes de caractères. Plus précisément, n’écrivez jamais de code comme celui-ci:

def mafct(a, b=[]):     # Non!
    ...

Si vous faites cela, vous pouvez rencontrer toutes sortes de problèmes si la valeur par défaut échappe à la fonction et est modifiée. De telles modifications modifieront de façon permanente la valeur par défaut lors de futurs appels de fonctions. Par exemple:

>>> def mafct(a, b=[]):
...     print(b)
...     return b
...
>>> x = mafct(1)
>>> x
[]
>>> x.append(99)
>>> x.append('Yellow!')
>>> x
[99, 'Yow!']
>>> mafct(1)       # La liste modifiée est retournée !
[99, 'Yellow!']
>>>

Ce n’est probablement pas ce que vous voulez. Pour éviter cela, il est préférable d’assigner None comme valeur par défaut et d’ajouter une vérification à l’intérieur de la fonction, comme indiqué dans la solution.

L’utilisation de l’opérateur is lorsque le test pour None est une partie critique de ce code. Parfois, les gens font cette erreur:

def mafct(a, b=None):
    if not b:      #  Non ! Utiliser à la place " b is None".
        b = []
    ...

Le problème ici est que bien que None s’évalue à False, beaucoup d’autres objets (par exemple, des chaînes de caractères de longueur nulle, des listes, des tuples, des dictionnaires, etc) sont aussi évalués à False.

Par conséquent, le test que nous venons de montrer traiterait comme False certains entrées comme manquantes. Par exemple:

>>> mafct(1)        # OK
>>> x = []
>>> mafct(1, x)     # Erreur silencieuse. x valeur écrasée par défaut.
>>> mafct(1, 0)     # Silent error. 0 ignored
>>> mafct(1, '')    # Silent error. "ignored
>>>

La dernière partie de cet article est quelque chose d’assez subtil – une fonction qui teste pour voir si une valeur (n’importe quelle valeur) a été fournie à un argument facultatif ou non.

La partie délicate ici est que vous ne pouvez pas utiliser une valeur par défaut de None, 0 ou False pour tester la présence d’un argument fourni par l’utilisateur (puisque toutes ces valeurs sont parfaitement valides qu’un utilisateur pourrait les fournir).

Ainsi, vous avez besoin de quelque chose d’autre pour tester contre. Pour résoudre ce problème, vous pouvez créer une instance privée unique d’objet, comme indiqué dans la solution (la variable _no_value).

Dans la fonction, vous vérifiez ensuite l’identité de l’argument fourni par rapport à cette valeur spéciale pour voir si un argument a été fourni ou non.

Le raisonnement ici est qu’il serait extrêmement improbable pour un utilisateur de passer l’instance _no_value en tant que valeur d’entrée. Par conséquent, elle devient une valeur sûre pour vérifier si vous essayez de déterminer si un argument a été fourni ou non.

L’utilisation de object() peut sembler assez inhabituelle ici. object est une classe qui sert de classe de base commune pour presque tous les objets en Python.

Vous pouvez créer des instances de la classe object, mais elles sont totalement inintéressantes, car elles n’ont pas de méthodes notables ni de données d’instance (parce qu’il n’y a pas de dictionnaire d’instance sous-jacent, vous ne pouvez même pas définir d’attributs).

La seule chose que vous pouvez faire, c’est faire des tests d’identité. Ceci les rend utiles en tant que valeurs spéciales, comme indiqué dans la solution.

LAISSER UN COMMENTAIRE

Please enter your comment!
Please enter your name here