Проблема с несвязанной локальной переменной в Python

#python #function #exception #bug-tracking #local-variables

#python #функция #исключение #отслеживание ошибок #локальные переменные

Вопрос:

У меня есть следующий фрагмент кода:

 def isolation_level(level):
    def decorator(fn):
        def recur(level, *args, **kwargs):
            if connection.inside_block:
                if connection.isolation_level < level:
                    raise IsolationLevelError(connection)
                else:
                    fn(*args, **kwargs)
            else:
                connection.enter_block()
                try:
                    connection.set_isolation_level(level)
                    fn(*args, **kwargs)
                    connection.commit()
                except IsolationLevelError, e:
                    connection.rollback()
                    recur(e.level, *args, **kwargs)
                finally:
                    connection.leave_block()
        def newfn(*args, **kwargs):
            if level is None: # <<<< ERROR MESSAGE HERE, Unbound local variable `level`
                if len(args):
                    if hasattr(args[0], 'isolation_level'):
                        level = args[0].isolation_level
                elif kwargs.has_key('self'):
                    if hasattr(kwargs['self'], 'isolation_level'):
                        level = kwargs.pop('self', 1) 
            if connection.is_dirty():
                connection.commit()
            recur(level, *args, **kwargs)
        return newfn
    return decorator
  

На самом деле не имеет значения, что она делает, однако я публикую ее в исходном виде, поскольку я не смог воссоздать ситуацию с помощью чего-либо более простого.

Проблема в том, что при вызове я isolation_level(1)(some_func)(some, args, here) получаю Unbound local variable исключение в строке 21 (отмечено в списке). Я не понимаю, почему. Я попытался воссоздать ту же структуру функций и вызовов функций, которая не содержала бы всех деталей реализации, чтобы выяснить, что не так. Однако тогда я не получаю сообщение об исключении. Например, работает следующее:

 def outer(x=None):
    def outer2(y):
        def inner(x, *args, **kwargs):
            print x
            print y
            print args
            print kwargs
        def inner2(*args, **kwargs):
            if x is None:
                print "I'm confused"
            inner(x, *args, **kwargs)
        return inner2
    return outer2

outer(1)(2)(3, z=4)
  

С принтами:

 1
2
(3,)
{'z': 4}
  

Чего я не понимаю??

Редактировать

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

Комментарии:

1. Сообщение об ошибке можно воспроизвести в нескольких строках, и это легко, если знать, что его вызывает. Смотрите codepad.org/nI0vCx4L

2. @delnan: самый короткий код, который вызывает ошибку, — это использование x = x внутри внутренней функции 🙂

3. @Sven: Изначально это было так, но я решил сделать это более похожим на код OP.

Ответ №1:

Локальные переменные определяются во время компиляции: присвоения переменной level несколькими строками ниже строки, в которой возникает ошибка, делают эту переменную локальной для внутренней функции. Итак, строка

 if level is None:
  

на самом деле пытается получить доступ к переменной level во внутренней области видимости, но такая переменная еще не существует. В Python 3.x вы можете решить эту проблему, объявив

 nonlocal level
  

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

Комментарии:

1. Смотрите, я делаю то же самое в приведенном ниже списке, который я опубликовал. И это не приводит к сбою. Я не понимаю, в чем разница.

2. @julkiewicz: Нет, вы не делаете то же самое. В вашем втором фрагменте вообще нет назначений . (Присваивание представляет собой строку типа a = 2 . Эта строка в любом месте внутри функции создаст a локальную переменную в любом месте внутри этой функции.)

3. О, хорошо, значит, он определяет назначение. Я глупый. Спасибо. Я приму и добавлю еще одно редактирование. Спасибо.