¿Alguna forma de modificar el diccionario locals?

locals es una función incorporada que devuelve un diccionario de valores locales. La documentación dice:

Advertencia

Los contenidos de este diccionario no deben ser modificados; los cambios pueden no afectar los valores de las variables locales utilizadas por el intérprete.

Desafortunadamente, exec tiene el mismo problema en Python 3.0. ¿Hay alguna manera de evitar esto?

Caso de uso

    Considerar:

     @depends("a", "b", "c", "d", "e", "f") def test(): put_into_locals(test.dependencies) 

    Depende almacena las cadenas proporcionadas en sus argumentos en una lista test.dependences . Estas cadenas son claves en un diccionario d . Me gustaría poder escribir put_into_locals para que podamos extraer los valores de d y colocarlos en los locales. es posible?

    Acabo de probar exec y funciona en Python 2.6.2

     >>> def test(): ... exec "a = 5" ... print a ... >>> test() 5 

    Si está usando Python 3.x, ya no funciona porque los locales están optimizados como una matriz en tiempo de ejecución, en lugar de usar un diccionario.

    Cuando Python detecta la “statement exec”, obligará a Python a cambiar el almacenamiento local de la matriz al diccionario. Sin embargo, dado que “exec” es una función en Python 3.x, el comstackdor no puede hacer esta distinción ya que el usuario podría haber hecho algo como “exec = 123”.

    http://bugs.python.org/issue4831

    Modificar los locales de una función sobre la marcha no es posible sin varias consecuencias: normalmente, los locales de funciones no se almacenan en un diccionario, sino en una matriz, cuyos índices se determinan en el momento de la comstackción a partir de los locales conocidos. Esto choca al menos con nuevos locales agregados por exec. La antigua statement de exec evitó esto, porque el comstackdor sabía que si se producía un exec sin argumentos globales / locales en una función, ese espacio de nombres sería “no optimizado”, es decir, no utilizar la matriz locals. Como exec () es ahora una función normal, el comstackdor no sabe a qué “exec” puede estar vinculado, y por lo tanto, no se puede tratar, especialmente.

    Las variables locales son modificadas por sentencias de asignación.

    Si tiene claves de diccionario que son cadenas, no las convierta en variables locales, simplemente úselas como claves de diccionario.

    Si es absolutamente necesario tener variables locales haz esto.

     def aFunction( a, b, c, d, e, f ): # use a, b, c, d, e and f as local variables aFunction( **someDictWithKeys_a_b_c_d_e_f ) 

    Eso llenará algunas variables locales de tu diccionario sin hacer nada mágico.

    Esto no es posible. Creo que esto es para permitir optimizaciones de rendimiento más adelante. El código de bytes de Python hace referencia a los locales por índice, no por nombre; si se requiriera que las escrituras locales () pudieran ser de escritura, podría impedir que los intérpretes implementen algunas optimizaciones, o dificultarlas.

    Estoy bastante seguro de que no vas a encontrar ninguna API básica que garantice que puedes editar locales como este, porque si esa API pudiera hacerlo, locals () tampoco tendría esta restricción.

    No olvide que todos los locales deben existir en tiempo de comstackción; Si hace referencia a un nombre que no está vinculado a un local en tiempo de comstackción, el comstackdor asume que es global. No puedes “crear” locales después de la comstackción.

    Vea esta pregunta para una posible solución, pero es un problema grave y realmente no quiere hacer eso.

    Tenga en cuenta que hay un problema básico con su código de ejemplo:

     @depends("a", "b", "c", "d", "e", "f") def test(): put_into_locals(test.dependencies) 

    "test.dependencies" no se refiere a “f.dependencies” donde f es la función actual; está haciendo referencia al valor global real “prueba”. Eso significa que si usas más de un decorador:

     @memoize @depends("a", "b", "c", "d", "e", "f") def test(): put_into_locals(test.dependencies) 

    ya no funcionará, ya que “prueba” es la función envuelta de memoize, no depende de. Python realmente necesita una forma de referirse a “la función que se ejecuta actualmente” (y clase).

    Lo almacenaría en una variable:

     refs = locals() def set_pets(): global refs animals = ('dog', 'cat', 'fish', 'fox', 'monkey') for i in range(len(animals)): refs['pet_0%s' % i] = animals[i] set_pets() refs['pet_05']='bird' print(pet_00, pet_02, pet_04, pet_01, pet_03, pet_05 ) >> dog fish monkey cat fox bird 

    Y si quieres probar tu dictado antes de ponerlo en locals ():

     def set_pets(): global refs sandbox = {} animals = ('dog', 'cat', 'fish', 'fox', 'monkey') for i in range(len(animals)): sandbox['pet_0%s' % i] = animals[i] # Test sandboxed dict here refs.update( sandbox ) 

    Python 3.6.1 en MacOS Sierra

    No estoy seguro de si está sujeto a las mismas restricciones, pero puede obtener una referencia directa al marco actual (y desde allí, el diccionario de variables locales) a través del módulo de inspección:

     >>> import inspect >>> inspect.currentframe().f_locals['foo'] = 'bar' >>> dir() ['__builtins__', '__doc__', '__name__', '__package__', 'foo', 'inspect'] >>> foo 'bar'