Узнать имя переменной объекта, вызвавшего исключение - PullRequest
0 голосов
/ 05 октября 2018

Давайте рассмотрим следующий пример кода, который я буду использовать для вызова AttributeError в качестве примера.

def test(first, second):
    print("My age is " + first.age + " and my neighbour is " + second.age)

Скажем, у меня есть следующий класс.

class Dummy(object):
    def __init__(self):
        pass

Если я вызываюфункция с

d = Dummy()
d.__setattr__("age", "25")
test(d, Dummy())

Я получу AttributeError, потому что второй Dummy не имеет атрибута age.Это вызвано second.age.

Теперь у меня вопрос, есть ли способ узнать, как называется переменная, которая вызывает ошибку.Глядя на исходный код, очевидно, что это second, но как я могу это выяснить в попытке, кроме блока?

Ответы [ 3 ]

0 голосов
/ 05 октября 2018

В целях отладки обратите внимание, что сообщение об ошибке объясняет, что произошло.

obj = object()
print(obj.does_not_exist)

Сообщение об ошибке

AttributeError: 'object' object has no attribute 'does_not_exist'

Таким образом, ясно, какой атрибут вызвал исключение.Вы также можете восстановить эту информацию с помощью функции sys.exc_info, если считаете, что она может понадобиться во время выполнения.

Сузьте свой try-except

Если это вас не устраивает, знайте,что цель оператора try-except состоит в том, чтобы отлавливать исключения, которые ожидают .Таким образом, если два разных исключения могут возникнуть в одном и том же блоке, вы можете разделить его на два оператора try-except.

def test(first, second):
    try:
        first_age = first.age
    except AttributeError:
        # Do something if first doest not have attribute age

    try:
        second_age = second.age
    except AttributeError:
        # Do something if second does not have attribute age

    print("My age is " + first.age + " and my neighbour is " + second.age)

Использовать hasattr

Другой вариант - использоватьhasattr чтобы проверить, существует ли атрибут.

def test(first, second):
    if not hasattr(first, 'age'):
        # Do something

    if not hasattr(second, 'age'):
        # Do something else

    print("My age is " + first.age + " and my neighbour is " + second.age)
0 голосов
/ 05 октября 2018

Хорошо, я нашел решение, которое также должно работать, если класс не находится под моим контролем.Это решение предназначено только для AttributeError, но должно быть расширяемым в случае необходимости обнаружения других ошибок.

У нас все еще одна и та же функция тестирования и тот же класс Dummy

def test(first, second):
    print("My name is " + first.age + " and I am here with " + second.age)

class Dummy(object):
    def __init__(self):
        pass

Мы можемиспользуйте объект Proxy для переноса каждого значения, которое мы передаем в тестовую функцию.Этот прокси-объект записывает, видит ли он AttributeError, устанавливая флаг _had_exception.

class Proxy(object):
    def __init__(self, object_a):
        self._object_a = object_a
        self._had_exception: bool = False

    def __getattribute__(self, name):
        if name == "_had_exception":
            return object.__getattribute__(self, name)

        obj = object.__getattribute__(self, '_object_a')
        try:
            return getattr(obj, name)
        except AttributeError as e:
            # Flag this object as a cause for an exception
            self._had_exception = True 
            raise e

И вызов функции выглядит следующим образом

d = Dummy()
d.__setattr__("age", "25")
p1 = Proxy(d)
p2 = Proxy(Dummy())
try:
    test(p1, p2)
except AttributeError as e:
    # Get the local variables from when the Error happened
    locals = e.__traceback__.tb_next.tb_frame.f_locals
    offender_names = []

    # Check if one of the local items is the same 
    # as one of our inputs that caused an Error
    for key, val in locals.items():
        if p1._had_exception:
            if p1 is val:
                offender_names.append(key)

        if p2._had_exception:
            if p2 is val:
                offender_names.append(key)

    print(offender_names) # ['second']

Конечный результатсписок со всеми именами локальных переменных - используемых в вызываемой функции - которые соответствуют нашим упакованным входам, что вызвало исключение.

0 голосов
/ 05 октября 2018

Вы можете изменить определение test, чтобы разделить доступ к атрибутам:

def test(first, second):
    f_age = first.age
    s_age = second.age
    print(f"My age is {f_age} and my neighbour is {s_age}")

Затем, когда вы вызовете test, вы сможете отследить его до определенной строки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...