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
.
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 Subscript
astunparse
) 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
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.
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.
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')