Soy bastante nuevo en Python y me pregunto cómo funcionan las variables locales. Comencemos con un ejemplo de un método simple:

def do_sth():
    local_dict = { 'a': 1, 'b': 2, 'c': 3, ... }
    ...

Supongamos que local_dict se usa como una especie de variable constante. Y aquí está la pregunta: ¿se crea cada vez que se invoca do_sth () o se crea una vez y se mantiene en algún lugar interno de do_sth ()?

3
m3diumendi4n 11 may. 2016 a las 23:15

5 respuestas

La mejor respuesta

Puede ver lo que hace el intérprete usando el módulo dis:

def do_sth():
    d = {'a':2, 'b':3}
    print(id(d))


import dis
dis.dis(do_sth)

Imprimirá

  2           0 BUILD_MAP                2
              3 LOAD_CONST               1 (2)
              6 LOAD_CONST               2 ('a')
              9 STORE_MAP           
             10 LOAD_CONST               3 (3)
             13 LOAD_CONST               4 ('b')
             16 STORE_MAP           
             17 STORE_FAST               0 (d)

  3          20 LOAD_GLOBAL              0 (id)
             23 LOAD_FAST                0 (d)
             26 CALL_FUNCTION            1
             29 PRINT_ITEM          
             30 PRINT_NEWLINE       
             31 LOAD_CONST               0 (None)
             34 RETURN_VALUE        

Lo que muestra que el intérprete está reconstruyendo el valor cada vez que se llama a la función.

6
thebjorn 11 may. 2016 a las 20:26

Las variables locales siempre se crean en el ámbito de una función. El recolector de basura los recopila una vez que el contador del programa sale de la función (alcance de la variable local).

global_dict = []

def do_sth():
    local_dict = { 'a': 1, 'b': 2, 'c': 3, ... }
    ...

def do_other_task():
   #local_dict not visible. It's outside of scope. 
   global global_dict # fetch memory address of previously declared global_dict
   global_dict = { 'a': 1, 'b': 2, 'c': 3, ... }
1
LAL 11 may. 2016 a las 20:24

Sería fácil verificar con el operador is:

def test():
    return {'a': 1, 'b': 2, 'c': 3}

>>> test() is test() #did both produce the same object in memory space?
False

Esto tiene mucho sentido para los objetos mutables o el argumento por defecto mutable

Sin embargo, hay valores que se almacenan como constantes y se pueden ver usando dis ya que las constantes se cargan con el LOAD_CONST código de byte:

>>> dis.dis(lambda:1)
  1           0 LOAD_CONST               1 (1)
              3 RETURN_VALUE
>>> dis.dis(lambda:(1,True, 10*1000, "a"))
  1           0 LOAD_CONST               7 ((1, True, 10000, 'a'))
              3 RETURN_VALUE
>>> dis.dis(lambda:[1,2,3]) #list is mutable
  1           0 LOAD_CONST               1 (1)
              3 LOAD_CONST               2 (2)
              6 LOAD_CONST               3 (3)
              9 BUILD_LIST               3
             12 RETURN_VALUE
>>> dis.dis(lambda:{"a":1}) #dict is also mutable
  1           0 LOAD_CONST               1 ('a')
              3 LOAD_CONST               2 (1)
              6 BUILD_MAP                1
              9 RETURN_VALUE
1
Community 23 may. 2017 a las 12:31

Las otras respuestas son correctas, solo agregaría esto.

Prefiero hacer, en tal caso, cualquiera:

constant_dict = { 'a': 1, 'b': 2, 'c': 3, ... }
def do_sth():
    # do something reading constant_dict

do_sth()

O pero menos preferible :

def do_sth(local_dict = { 'a': 1, 'b': 2, 'c': 3, ... }):
    # do something reading local_dict

do_sth()

En ambos casos, python no tendría que reasignar la memoria para la variable, porque fue declarada y asignada cuando el intérprete lee el archivo python.

Corrígeme si estoy equivocada.

0
Anthony Perot 11 may. 2016 a las 20:44

Cada vez que se invoca do_sth(). Esto es importante porque le permite realizar modificaciones en local_dict dentro de la función y no tendrá ningún efecto en otras llamadas. El intérprete no es lo suficientemente inteligente como para ver si no lo va a cambiar, especialmente porque Python es tan dinámico que hay algunas formas muy indirectas en que puede hacer que cambie.

Así es como puede probarse a sí mismo que el diccionario se sigue recreando:

def print_3():
    print(3)

def do_sth():
    local_dict = {'a': print_3()}

do_sth()
do_sth()
do_sth()

Esto imprime 3 ... 3 veces.

Creo que las variables globales están bien para optimizar esto, pero si realmente quieres, ¿qué tal esto?

def do_sth():
    return do_sth.local_dict

do_sth.local_dict = {'a': print_3()}

Técnicamente, todos pueden acceder a esto, pero es más claro a qué pertenece.

3
Alex Hall 11 may. 2016 a las 20:47