Как я могу узнать, какой индекс находится вне диапазона?

в случае IndexError, есть ли способ, чтобы сказать, какой объект на линии "вне диапазона"?

рассмотрим этот код:

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

x, y = get_values_from_somewhere()

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

в случае x или y и IndexError попадает, Я хотел бы знать, какой из a или b вне диапазона (поэтому я могу выполнять различные действия в except блок).

ясно, что я мог бы сравнить x и y to len(a) и len(b) соответственно, но я любопытно, есть ли другой способ сделать это с помощью IndexError.

5 ответов


более надежным подходом было бы нажать на объект traceback, возвращаемый sys.exc_info(), извлеките код, указанный именем файла и номером строки из фрейма, используйте ast.parse чтобы разобрать строку, подкласс ast.NodeVisitor найти все Subscript узлы, unparse (с astunparse) и eval узлы с глобальными и локальными переменными кадра, чтобы увидеть, какой из узлов вызывает исключение, и распечатать оскорбительное выражение:

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()

этот выходы:

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

есть способ, но я бы не счел его очень надежным. Существует тонкая разница в сообщениях об ошибках:

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

но если ошибка находится на левой стороне:

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

обратите внимание на "назначение" в сообщении.

Итак, вы можете сделать что-то вроде:

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")

выход:

# Error on left hand side

опять же, я бы не доверял ему слишком много, он потерпит неудачу, если сообщение изменится в другой версии Питон.


на эти части исходного кода, эти разные сообщения действительно являются единственной разницей между двумя ошибками.


на IndexError exception не хранит информацию о том, что вызвало исключение. Его единственными данными является сообщение об ошибке. Для этого вам нужно создать свой код.

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:
    ...

Я хочу добавить, что я пытался восстановить виновника через inspect.frame и не смог этого сделать. Таким образом, я подозреваю, что на самом деле нет надежного способа.

наконец, обратите внимание, что это специфично для IndexError поскольку другие исключения могут содержать необходимую информацию для вывода о том, что их вызвало. К примеру KeyError содержит ключ, который его поднял.


что-то вроде этого возможно?

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')

нет, нет способа вывести, какой из двух выходит за рамки, используя индексатор.