Quelle valeur retourner quand on ne trouve rien en Python ? | Sam & Max: Python, Django, Git et du cul

Quelle valeur retourner quand on ne trouve rien en Python ?

Hier je me suis dit que maintenant je mettrai de la musique à écouter pendant le tuto. Parce que. Donc détendez-vous avez un petit morceau de K’s Choice :

[iframe width="420" height="315" src="http://www.youtube.com/embed/7kNzDk2CPHI" frameborder="0" allowfullscreen]

Techniquement une fonction Python ne peut pas ne rien retourner. Il n’existe rien de telle qu’un procédure dans ce langage. Si vous ne donnez aucune valeur de retour, elle va retourner None.

>>> def metal():
...     1 + 1
...     
>>> print metal()
None

Ne rien retourner, c’est donc choisir de retourner None. Or la valeur de retour est un choix d’API, et va dicter comme les gens vont utiliser votre fonction. Il convient donc de bien choisir sa valeur de retour “à vide”.

Fonction qui produit un résultat

La fonction qui produit un résultat, par exemple on lui donne deux valeur, et elle calcule une troisième, est le cas le plus facile.

Si la valeur peut être calculée, on retourne la valeur. Sinon, c’est qu’il y a un problème avec les arguments. Comme Python n’est pas un langage compilé, on ne check pas la nature des arguments. De plus parfois ce n’est pas tant un problème de nature que de valeur (comme le domaine de définition en maths). Dans ce cas, le mieux est de lever une exception (souvent ValueError ou un descendant) :

def by_poison(arg1, arg2):

    # faire l'opération ici

    # ah, ça marche pas ?

    raise ValueError("{} et {} doivent être bidule truc sinon ça ne marche pas".format(arg1, arg2))

On évite la plupart du temps les valeurs qui veulent dire sémantiquement “mauvaise opération” comme NaN en Python. On préférera lever une exception. None est rarement une bonne idée ici.

Fonction qui cherche un résultat

Ce genre de fonction ne va pas lever une exception sous prétexte qu’elle n’a pas trouvé un truc. Ne pas trouver est un état tout à fait normal (à moins que la présence soit requise, mais dans ce cas lever l’exception ne doit pas être à ce niveau mais à celui plus haut de toute façon).

Si c’est une vérification de présence, on retourne juste True / False :

def is_inside(value, other_value):

    # vérifier que value est dans other value

    return True # ou False

Notez que si vous faites les choses proprement, il vaut mieux overrider __contains__ pour permettre d’utiliser in directement.

Si votre recherche doit retourner une liste d’items (comme les lignes dans une base de données), dans ce cas quand on ne trouve rien, il vaut mieux retourner une liste vide. Cela permet de faire une boucle for sur le résultat dans tous les cas sans vérification ni side effect.

def inignition(query):

    # chercher un truc, et si on trouve le retourner

    # et tout à la fin, au cas où
    return []

Si vous cherchez un élément en particulier (genre un nombre premier dans la liste donnée), le mieux est de retourner None si il ne fait pas partie des données retournable. Si None fait partie des données trouvable, on retourne au fait de lever des exceptions :(. Mais c’est qu’il y a un problème ailleurs, car None ne devrait pas être une donnée significative.

def fed(truc, bidule):

    # chercher un truc, et si on trouve le retourner

    # et tout à la fin, au cas où
    return None

Certains cas particuliers demandent de retourner le même type que celui recherché. Il n’y a pas de règle pour ça, il va falloir utiliser votre tête, pour faire quelque chose de cohérent.

Par exemple, rechercher la chaîne la plus longue dans une séquence doit-il retourner None si aucune n’est trouvée, ou une chaîne vide ? Il n’y a pas de bonne réponse, c’est selon la sémantique de votre appli. Si elle traite uniquement avec des types string, une chaîne vide peut avoir du sens. Si c’est la présence ou l’absence de chaîne la plus longue qui est importante, alors None sera plus approprié.

A l’inverse, des fonctions comme “trouver la position” ne doit pas retourner -1 comme dans la plupart des autres langages. -1 est en effet très significatif en Python (pour le slicing par exemple). Évitez donc d’utiliser le même type pour dire “rien n’est trouvé” si la valeur risque d’être significative. Dans ce cas choisissez None.

megalist[:littlelist.search(truc)] # si littlelist.search(truc) retourn -1 c'est la merde !

La bibliothèque Python a tendance à lever des exception à tout va :

>>> s = 'Tasse'
>>> s.index('p')
Traceback (most recent call last):
  File "", line 1, in 
    s.index('p')
ValueError: substring not found

C’est un comportement acceptable, mais ce n’est pas le plus pratique. Vous allez voir pourquoi.

Valeur par défaut

Retourner None quand on ne trouve rien peut résulter en une API extrêmement agréable car l’action la plus utilisée comme contre mesure est d’avoir une valeur par défaut. Prenez pas exemple un dictionnaire. Récupérer un élement et assigner une valeur par défaut si il manque ressemble à ça avec une exception :

>>> dicos = {'petit larousse' : 'dans la tête', 'gros robert' : 'dans le derriere'}
>>> dicos['urban dictionary'] # un entrée inexistante lève l'exception KeyError
Traceback (most recent call last):
  File "", line 1, in 
    dicos['urban dictionary']
KeyError: 'urban dictionary'
>>> try: # du coup on doit faire un try
...     ou_ca = dicos['urban dictionary']
... except KeyError:
...     ou_ca = 'sur le net'
...     
>>> ou_ca
'sur le net'

Mais Python permet un raccourcis pour ça, vraiment sympas :

>>> dicos.get('urban dictionary', 'sur le net')
'sur le net'

La méthode get() permet de récupérer une valeur, et si la clé n’existe pas, elle retourne le deuxième argument. Or, par défaut, le second argument est None :

>>> print dicos.get('urban dictionary')
None

Donc si vous avez une fonction qui cherche quelque chose et qui risque de ne pas trouver, il peut être très pratique de proposer une argument comme valeur de retour par défaut, et de le mettre à None :

def and_blind(cherche, default=None):

     if je_trouve_pas_cherche:
        return default

  5 comments for “Quelle valeur retourner quand on ne trouve rien en Python ?

  1. Krypted
    30/01/2013 at 11:07

    Encore une fois un article simple, lisible et intéressant. Au final c’est que de la logique mais c’est vrai que rappeler les bases ne fait jamais de mal !

    C’est également une très bonne idée de mettre un morceau de musique à écouter en lisant l’article. A renouveler dès que possible! :)

  2. Krypted
    30/01/2013 at 11:10

    Par contre le Mario est bien mais la fenêtre est légèrement trop petite et quand on appuie sur haut c’est le page entière qui scroll… pas très pratique. (Testé sous Chrome)

  3. Etienne
    30/01/2013 at 14:10

    En fait, quand tu ne trouves pas de sujet d’article tu devrais retourner None. Au lieu de quoi tu retournes un article qui parle de ce qu’on retourne quand on ne trouve rien en python.

    Qu’est-ce qu’on doit faire? Ce que tu dis, ou ce que tu fais?

  4. Sam
    30/01/2013 at 17:11

    L’article n’est pas la valeur de retour, c’est la docstring. Débutant va.

  5. Kontre
    01/02/2013 at 04:04

    Les vidéos youtube dans les docstrings, j’avais encore jamais fait ! ^^

Leave a Reply

Your email address will not be published. Required fields are marked *