2009-09-20 10 views
11

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

Advertencia

El contenido de este diccionario debe no ser modificado; los cambios pueden no afectar afectar los valores de las variables locales utilizados por el intérprete.

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

caso de uso

considerar:

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

depende tiendas de las cadenas provistas 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 poder extraer los valores de d y ponerlos en los locales. es posible?

+0

Enlace a la documentación pertinente: https://docs.python.org/2/library/functions.html#locals –

+0

¿por qué 'prueba. dependencies = ["a", "b", "c", "d", "e", "f"] 'trabajo y luego decorar la tarea que escribí anteriormente a su función' test() '? –

+0

¿logró actualizar/modificar locales o no? –

Respuesta

11

Acabo de probar Exec y funciona en Python 2.6.2

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

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

Cuando Python detecta la "declaración ejecutiva", forzará 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 compilador no puede hacer esta distinción ya que el usuario podría haber hecho algo como "exec = 123".

http://bugs.python.org/issue4831

Para modificar los locales de una función en la marcha no es posible sin varias consecuencias: normalmente, locales de funciones no se almacenan en un diccionario , pero una matriz, cuya índices se determinan en el tiempo de compilación desde las configuraciones regionales conocidas. Esto colisiona al menos con nuevos locales añadidos por exec. La vieja sentencia exec eludido esto, debido a que el compilador sabía que si un ejecutivo sin globals/Locales args se produjo en una función , ese espacio de nombres sería "no optimizado", es decir, no utilizando la matriz locales. Como exec() es ahora una función normal , el compilador no sabe a qué se debe vincular "exec", y , por lo que no se puede tratar especialmente.

+0

Creo que es bastante concluyente que simplemente no es posible – Casebash

+1

@Casebash, es probable que sea posible, solo requiere hackeo de códigos de bytes o Python 2.x – Unknown

+0

Está bien, dejaré esta pregunta sin resolver por ahora – Casebash

-1

no estoy seguro de si está sujeto a las mismas restricciones, pero se puede obtener una referencia directa a la trama actual (y de ahí, las variables locales del diccionario) 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' 
+7

Esto es exactamente lo mismo que locals(); 'inspect.currentframe(). f_locals es locals()' es verdadero. –

+1

Esto no es ** totalmente ** incorrecto, pero solo funciona cuando el marco es el más alto, es decir, el alcance global. No funcionará dentro de los ámbitos locales. – bendtherules

3

Esto no es posible. Creo que esto es para permitir optimizaciones de rendimiento más adelante. Python bytecode hace referencia a los locales por índice, no por nombre; si se requería que los locales() tuvieran permisos de escritura, esto podría evitar que los intérpretes implementaran algunas optimizaciones o las hicieran más difíciles.

Estoy bastante seguro de que no encontrarás ninguna API básica que garantice que puedas editar locales así, porque si esa API pudiera hacerlo, los locals() tampoco tendrían esta restricción.

No olvide que todos los locales deben existir en tiempo de compilación; si hace referencia a un nombre que no está vinculado a un local en tiempo de compilación, el compilador supone que es global. No puede "crear" locales después de la compilación.

Vea this question para una posible solución, pero es un truco serio y realmente no quiere hacer eso.

Tenga en cuenta que hay un problema básico con el 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 a la "prueba" de valor global real. Esto significa que si se utiliza más de un decorador:

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

ya no va a trabajar, ya que "prueba" es memoize su función envuelto, no depende de. Python realmente necesita una forma de referirse a "la función actualmente en ejecución" (y clase).

5

Las variables locales se modifican mediante instrucciones de asignación.

Si tiene claves de diccionario que son cadenas, no las convierta también en variables locales, simplemente utilícelas como claves de diccionario.

Si absolutamente debe tener variables locales hacer 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 poblará algunas variables locales de su diccionario sin hacer nada mágico.

+0

justo lo que estaba pensando; también podría crear dinámicamente una función; ver ayuda (types.FunctionType) – gatoatigrado

+4

Esta es una idea interesante. Sin embargo, hay muchas aplicaciones en las que el diccionario en realidad contiene muchas otras variables (que no son necesarias por 'aFunction()'), lo que hace que la definición actual de 'aFunction()' se rompa. Una generalización útil es: 'aFunción (a, b, c, d, e, f, ** kwargs)'. – EOL

+0

@EOL: ¿Variables de parámetros adicionales hacen que una función se rompa? Eso es difícil de imaginar. Algunas variables adicionales deberían ser, bueno, solo variables. Una función que se rompe debido a unas pocas variables adicionales tiene un diseño notablemente pobre. Sería mejor arreglar esta función. –

1

Me almacenarlo 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 desea probar su dict antes de meterla en los locales():

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

Cuestiones relacionadas