Несвязанная проблема локальной переменной в Python - PullRequest
5 голосов
/ 30 апреля 2011

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

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}

Что мне не хватает ??

EDIT

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

1 Ответ

9 голосов
/ 30 апреля 2011

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

if level is None:

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

nonlocal level

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

...