Python: LOAD_FAST против LOAD_DEREF с добавлением на месте

В прошлую пятницу я пошел на собеседование и должен был ответить на следующий вопрос: почему этот код вызывает исключение (UnboundLocalError: local variable 'var' referenced before assignment на строке, содержащейvar += 1)?

def outer():
    var = 1

    def inner():
        var += 1
        return var

    return inner

Я не мог дать правильный ответ; этот факт очень расстроил меня, и когда я пришел домой, я очень старался найти правильный ответ. Ну яиметь нашел ответ, но теперь есть кое-что еще, что смущает меня.

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

Итак, рассмотрим этот код. Внутренняя функция - это закрытие Python, иvar не является местным дляouter - хранится в ячейке (а затем извлекается из ячейки):

def outer():
    var = 1

    def inner():
        return var

    return inner

Разборка выглядит так:

0  LOAD_CONST               1 (1)
3  STORE_DEREF              0 (var)  # not STORE_FAST

6  LOAD_CLOSURE             0 (var)
9  BUILD_TUPLE              1
12 LOAD_CONST               2 (<code object inner at 0x10796c810)
15 LOAD_CONST               3 ('outer.<locals>.inner')
18 MAKE_CLOSURE             0
21 STORE_FAST               0 (inner)

24 LOAD_FAST                0 (inner)
27 RETURN_VALUE

recursing into <code object inner at 0x10796c810:

0  LOAD_DEREF               0 (var)  # same thing
3  RETURN_VALUE

Это меняется, когда мы пытаемся связать что-то еще сvar внутри внутренней функции:

def outer():
    var = 1

    def inner():
        var = 2
        return var

    return inner

Еще раз разборка:

0  LOAD_CONST               1 (1)
3  STORE_FAST               0 (var)  # this one changed
6  LOAD_CONST               2 (<code object inner at 0x1084a1810)
9  LOAD_CONST               3 ('outer.<locals>.inner')
12 MAKE_FUNCTION            0  # AND not MAKE_CLOSURE
15 STORE_FAST               1 (inner)

18 LOAD_FAST                1 (inner)
21 RETURN_VALUE

recursing into <code object inner at 0x1084a1810:

0  LOAD_CONST               1 (2)
3  STORE_FAST               0 (var)  # 'var' is supposed to be local

6  LOAD_FAST                0 (var)  
9  RETURN_VALUE

Мы хранимvar локально, что соответствует тому, что сказано в документации:присваивание имен всегда входит в самую внутреннюю область видимости.

Теперь, когда мы пытаемся сделать приращениеvar += 1противныйLOAD_FAST появляется, который пытается получитьvar отinnerЛокальная сфера применения:

14 LOAD_FAST                0 (var)
17 LOAD_CONST               2 (2)
20 INPLACE_ADD
21 STORE_FAST               0 (var)

И конечно мы получаем ошибку. Сейчас,вот что я не понимаю: почему мы не можем получитьvar сLOAD_DEREFи затем храните его внутриinnerразмах сSTORE_FAST? Я имею в виду, это, кажется, О.К. с заданием «внутренней области видимости», и в то же время это несколько более интуитивно желательно. По крайней мере+= код будет делать то, что мы хотим, и я не могу придумать ситуацию, в которой описанный подход может что-то испортить.

Ты можешь? Я чувствую, что мне чего-то не хватает здесь.

Ответы на вопрос(0)

Ваш ответ на вопрос