Python: LOAD_FAST vs. LOAD_DEREF con adición in situ

El viernes pasado fui a una entrevista de trabajo y tuve que responder la siguiente pregunta: ¿por qué este código genera una excepción?UnboundLocalError: local variable 'var' referenced before assignment en la línea que contienevar += 1)?

def outer():
    var = 1

    def inner():
        var += 1
        return var

    return inner

No pude dar una respuesta adecuada; este hecho realmente me molestó, y cuando llegué a casa intenté con todas mis fuerzas encontrar una respuesta adecuada. Bueno yotener Encontré la respuesta, pero ahora hay algo más que me confunde.

Tengo que decir de antemano que mi pregunta es más sobre las decisiones tomadas al diseñar el lenguaje, no sobre cómo funciona.

Entonces, considere este código. La función interna es un cierre de pitón, yvar no es local paraouter - se almacena en una celda (y luego se recupera de una celda):

def outer():
    var = 1

    def inner():
        return var

    return inner

El desmontaje se ve así:

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

Esto cambia cuando intentamos vincular algo más avar dentro de la función interna:

def outer():
    var = 1

    def inner():
        var = 2
        return var

    return inner

Una vez más el desmontaje:

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

Almacenamosvar localmente, que cumple con lo que se dice en la documentación:las asignaciones a los nombres siempre van al ámbito más interno.

Ahora, cuando intentamos hacer un incrementovar += 1, un desagradableLOAD_FAST aparece, que trata de obtenervar deinnerAlcance local:

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

Y, por supuesto, tenemos un error. Ahora,esto es lo que no entiendo: por qué no podemos recuperarvar con unLOAD_DEREFy LUEGO guárdelo adentroinneralcance de un conSTORE_FAST? Quiero decir, esto parece estar bien. con la tarea de "alcance más interno", y al mismo tiempo es algo más intuitivamente deseable. Al menos el+= el código haría lo que queremos que haga, y no puedo pensar en una situación en la que el enfoque descrito pueda estropear algo.

¿Puedes? Siento que me falta algo aquí.

Respuestas a la pregunta(3)

Su respuesta a la pregunta