Как я могу узнать, какой индекс находится вне диапазона? - PullRequest
0 голосов
/ 14 сентября 2018

В случае ошибки 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 с len(a) и len(b) соответственно, но мне любопытно, есть ли другой способ сделать это, используя IndexError.

Ответы [ 5 ]

0 голосов
/ 14 сентября 2018

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

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
0 голосов
/ 14 сентября 2018

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

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 голосов
/ 14 сентября 2018

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

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 содержит ключ, который его поднял.

0 голосов
/ 14 сентября 2018

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

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

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


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

0 голосов
/ 14 сентября 2018

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

...