177 votes

Meilleur moyen de gérer list.index(might-not-exist) en python ?

J'ai un code qui ressemble à quelque chose comme ceci :

thing_index = thing_list.index(thing)
otherfunction(thing_list, thing_index)

Ok, c'est simplifié mais vous voyez l'idée. Maintenant thing pourrait ne pas être dans la liste, dans ce cas je veux passer -1 comme thing_index . Dans d'autres langues, c'est ce à quoi on s'attendrait index() à retourner s'il ne trouvait pas l'élément. En fait, il lance un ValueError .

Je pourrais le faire :

try:
    thing_index = thing_list.index(thing)
except ValueError:
    thing_index = -1
otherfunction(thing_list, thing_index)

Mais ça semble sale, en plus je ne sais pas si ValueError pourrait être soulevée pour une autre raison. J'ai trouvé la solution suivante, basée sur des fonctions de générateur, mais elle semble un peu complexe :

thing_index = ( [(i for i in xrange(len(thing_list)) if thing_list[i]==thing)] or [-1] )[0]

Existe-t-il un moyen plus propre d'obtenir la même chose ? Supposons que la liste ne soit pas triée.

6 votes

"...dans ce cas, je veux passer -1 comme thing_index ." - Ce n'est absolument pas pythonique. Passer une valeur symbolique (sans signification) au cas où une opération échouerait est mal vu - les exceptions sont vraiment la bonne solution ici. D'autant plus que thing_list[-1] est une expression valide, signifiant la dernière entrée de la liste.

1 votes

@jellybean : facepalm ...repérer le codeur java :P

5 votes

@Tim : il y a str.find qui fait exactement cela : elle renvoie -1 lorsque l'aiguille ne se trouve pas dans le sujet.

85voto

Emil Ivanov Points 18594
thing_index = thing_list.index(elem) if elem in thing_list else -1

Une ligne. Simple. Pas d'exceptions.

77 votes

C'est simple, mais cela permet d'effectuer deux recherches linéaires et, bien que les performances ne soient pas un problème en soi, cela semble excessif.

6 votes

@Draemon : D'accord - cela fera 2 passes - mais il est peu probable que sur une base de code de mille lignes, celle-ci soit le goulot d'étranglement. :) On peut toujours opter pour une solution impérative avec for .

1 votes

Avec lambdas indexOf = lambda item,list_ : list_.index(item) if item in list_ else -1 # OR None

84voto

SilentGhost Points 79627

Il n'y a rien de "sale" à utiliser la clause try-except. C'est la méthode pythonique. ValueError sera soulevée par le .index uniquement, car c'est le seul code que vous avez là !

Pour répondre au commentaire :
En Python, il est plus facile de demander le pardon que d'obtenir la permission la philosophie est bien établie, et pas de index ne soulèvera pas ce type d'erreur pour d'autres problèmes. Je ne pense pas qu'il y en ait.

48 votes

Les exceptions doivent être réservées aux cas exceptionnels, et ce n'est pas le cas ici. Je n'aurais pas un tel problème si l'exception était plus spécifique que ValueError.

1 votes

Je sais que ça ne peut être lancé qu'à partir de ça. méthode mais est-il garanti qu'il ne sera lancé que pour ce type d'événement ? raison ? Non pas que je puisse penser à une autre raison pour laquelle l'indice échouerait mais les exceptions ne sont-elles pas exactement pour ces choses auxquelles vous ne pensez pas ?

0 votes

@Draemon : C'est pourquoi il vérifie seulement ValueError et pas n'importe quelle forme d'exception.

22voto

Ross Rogers Points 5619

El dict type a un get función où, si la clé n'existe pas dans le dictionnaire, le deuxième argument de la commande get est la valeur qu'il doit retourner. De même, il existe setdefault qui renvoie la valeur dans le champ dict si la clé existe, sinon elle définit la valeur en fonction de votre paramètre par défaut et renvoie ensuite votre paramètre par défaut.

Vous pourriez étendre le list pour avoir un getindexdefault méthode.

class SuperDuperList(list):
    def getindexdefault(self, elem, default):
        try:
            thing_index = self.index(elem)
            return thing_index
        except ValueError:
            return default

Qui pourrait alors être utilisé comme :

mylist = SuperDuperList([0,1,2])
index = mylist.getindexdefault( 'asdf', -1 )

13voto

J.F. Sebastian Points 102961

Il n'y a rien de mal dans votre code qui utilise ValueError . Voici encore une autre phrase, si vous voulez éviter les exceptions :

thing_index = next((i for i, x in enumerate(thing_list) if x == thing), -1)

0 votes

Est-ce que c'est Python 2.6 ? Je sais que je ne l'ai pas mentionné, mais j'utilise la version 2.5. C'est probablement ce que je ferais en 2.6.

1 votes

@Draemon : Oui, next() existe dans Python 2.6+ . Mais elle est facile à implémenter pour 2.5, voir Implémentation de la fonction next() pour Python 2.5

7voto

Tendayi Mawushe Points 10335

Cette question relève de la philosophie du langage. En Java, par exemple, il y a toujours eu une tradition selon laquelle les exceptions ne devraient être utilisées que dans des "circonstances exceptionnelles", c'est-à-dire lorsque des erreurs se sont produites, plutôt que pour contrôle du débit . Au début, c'était pour des raisons de performance, car les exceptions Java étaient lentes, mais aujourd'hui, c'est devenu le style accepté.

En revanche, Python a toujours utilisé des exceptions pour indiquer le déroulement normal du programme, comme la levée d'un message de type ValueError comme nous en discutons ici. Il n'y a rien de "sale" à ce sujet dans le style Python et il y en a beaucoup d'autres là d'où il vient. Un exemple encore plus courant est StopIteration exception qui est soulevée par la méthode de l'itérateur. next() pour signaler qu'il n'y a pas d'autres valeurs.

0 votes

En fait, le JDK lance chemin trop d'exceptions vérifiées, donc je ne suis pas sûr que cette philosophie soit réellement appliquée à Java. Je n'ai pas de problème per-se avec StopIteration parce que la signification de l'exception est clairement définie. ValueError est juste un peu trop générique.

0 votes

Je faisais référence à l'idée que les exceptions ne devraient pas être utilisées pour le contrôle du flux : c2.com/cgi/wiki?DontUseExceptionsForFlowControl Le nombre d'exceptions vérifiées dans Java, qui fait l'objet d'une toute autre discussion, n'est pas si important : mindview.net/Etc/Discussions/CheckedExceptions

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X