web-dev-qa-db-fra.com

Comment puis-je savoir quel index est hors limites?

Dans le cas d'une IndexError, existe-t-il un moyen de déterminer quel objet sur une ligne est «hors limites»?

Considérons ce code:

a = [1,2,3]
b = [1,2,3]

x, y = get_values_from_somewhere()

try:
   a[x] = b[y]
except IndexError as e:
   ....

Dans le cas où x ou y est trop grand et que IndexError soit attrapé, j'aimerais savoir lequel de a ou b est hors de portée (afin que je puisse effectuer différentes actions dans le bloc except.

Clairement, je pourrais comparer x et y à len(a) et len(b) respectivement, mais je suis curieux de savoir s'il existe un autre moyen de le faire en utilisant IndexError.

8
David Board

Une approche plus robuste consisterait à exploiter l'objet de traceback renvoyé par sys.exc_info(), à extraire le code indiqué par le nom de fichier et le numéro de ligne du cadre, à utiliser ast.parse pour analyser la ligne, sous-classe ast.NodeVisitor pour rechercher tous les nœuds Subscriptastunparse ) et évaluez les noeuds avec les variables globales et locales du cadre pour voir lequel des noeuds provoque une exception et imprimez l'expression incriminée:

import sys
import linecache
import ast
import astunparse

def find_index_error():
    tb = sys.exc_info()[2]
    frame = tb.tb_frame
    lineno = tb.tb_lineno
    filename = frame.f_code.co_filename
    line = linecache.getline(filename, lineno, frame.f_globals)
    class find_index_error_node(ast.NodeVisitor):
        def visit_Subscript(self, node):
            expr = astunparse.unparse(node).strip()
            try:
                eval(expr, frame.f_globals, frame.f_locals)
            except IndexError:
                print("%s causes IndexError" % expr)
    find_index_error_node().visit(ast.parse(line.strip(), filename))

a = [1,2,3]
b = [1,2,3]
x, y = 1, 2
def f():
    return 3
try:
    a[f() - 1] = b[f() + y] + a[x + 1] # can you guess which of them causes IndexError?
except IndexError:
    find_index_error()

Cela génère:

b[(f() + y)] causes IndexError
4
blhsing

Il y a un moyen, mais je ne le considérerais pas comme très robuste. Il y a une différence subtile dans les messages d'erreur:

a = [1,2,3]
b = [1,2,3]

a[2] = b[3]

---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-69-8e0d280b609d> in <module>()
      2 b = [1,2,3]
      3 
----> 4 a[2] = b[3]

IndexError: list index out of range

Mais si l'erreur est à gauche:

a[3] = b[2]

---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-68-9d66e07bc70d> in <module>()
      2 b = [1,2,3]
      3 
----> 4 a[3] = b[2]

IndexError: list assignment index out of range

Notez la «cession» dans le message.

Donc, vous pourriez faire quelque chose comme:

a = [1,2,3]
b = [1,2,3]

try:
    a[3] = b[2]
except IndexError as e:
    message = e.args[0]
    if 'assignment' in message:
        print("Error on left hand side")
    else:
        print("Error on right hand side")

Sortie:

# Error on left hand side

Encore une fois, je n'y ferais pas trop confiance, cela échouerait si le message était modifié dans une autre version de Python.


J'ai jeté un coup d'œil à ces parties du code source , ces différents messages sont vraiment la seule différence entre les deux erreurs.

8
Thierry Lathuille

L'exception IndexError ne stocke pas d'informations sur ce qui a déclenché l'exception. Sa seule donnée est un message d'erreur. Vous devez créer votre code pour le faire.

a = [1,2,3]
b = [1,2,3]

x, y = get_values_from_somewhere()

try:
    value = b[y]
except IndexError as e:
   ...

try:
    a[x] = value
except IndexError as e:
    ...

Je veux ajouter que j’ai tenté de récupérer le coupable par le biais de inspect.frame et que j’ai été incapable de le faire. Ainsi, je soupçonne qu’il n’ya vraiment pas de moyen robuste.

Enfin, notez que cela est spécifique à IndexError car d’autres exceptions peuvent contenir les informations nécessaires pour déduire ce qui les a provoquées. Par exemple, une KeyError contient la clé qui l'a soulevée.

5
Olivier Melançon

Quelque chose comme ça peut-être?

a = [1,2,3]
b = [2,3,4]
x = 5
y = 1

try:
    a[x] = b[y]
except IndexError:
    try:
        a[x]
        print('b caused indexerror')
    except IndexError:
        print('a caused indexerror')
0
Xnkr