2010-12-19 10 views
45

Parece que no puede usar el ejecutor en una función que tiene una subfunción ...¿Por qué el ejecutor no funciona en una función con una subfunción?

¿Alguien sabe por qué este código de Python no funciona? Recibo un error en el ejecutivo en test2. Además, sé que los ejecutivos no son un buen estilo, pero créanme, estoy usando el ejecutivo por una razón apropiada. No lo usaría de otra manera.

#!/usr/bin/env python 
# 

def test1(): 
    exec('print "hi from test1"') 

test1() 

def test2(): 
    """Test with a subfunction.""" 
    exec('print "hi from test2"') 
    def subfunction(): 
     return True 

test2() 

EDITAR: Reduje el error a tener una función en una subfunción. No tiene nada que ver con la palabra clave raise.

+0

Existe la misma limitación con 'import * from ...'. – osa

+0

me parece que las comprensiones dict se consideran subfunciones. – dbliss

Respuesta

58

Correcto. No puede usar exec en una función que tenga una subfunción, a menos que especifique un contexto. A partir de los documentos:

Si exec se utiliza en una función y la función contiene un bloque anidado con variables libres, el compilador levantar una SyntaxError a menos que el ejecutivo especifica explícitamente el local de espacio de nombres para el ejecutivo . (En otras palabras , "exec obj" sería ilegal, pero "obj ejecutivo en ns" sería legal.)

Hay una buena razón para esto, que probablemente me entiendo si no era domingo noche. Ahora, la siguiente pregunta: ¿Por qué estás usando el ejecutivo? Rara vez es necesario. Usted dice que tiene una buena razón. Me siento escéptico sobre eso. ;) Si tiene una buena razón, le diré la solución. :-P

Bueno, aquí está de todos modos:

def test2(): 
    """Test with a subfunction.""" 
    exec 'print "hi from test2"' in globals(), locals() 
    def subfunction(): 
     return True 
+0

Implementar una API humana y orientada a objetos para una base de datos NoSQL esotérica que solo tiene enlaces de muy bajo nivel a Python es un ejemplo. Sin embargo, una pregunta adicional para usted: ¿hay alguna forma de insertar el código '' exec '' ed en una clase específica? Si es así, ¿cómo? – chiffa

+1

@AndreiKucharavy No, 'exec' ejecuta el código, por lo que no existe el código" exec "-ed para inyectar. El código ejecutado es código ejecutado. Puedes compilar código e insertarlo, ¿quizás es eso lo que querías decir? Pero generalmente no necesitas compilarlo, solo lo inyectas. No hay ninguna razón obvia por la que deba ejecutar algo simplemente porque desea una API OO a una base de datos NoSQL. –

+0

Lo siento, mi mal: no se inyecta el código, pero se lo inserta, por lo que se puede llamar como parte de una clase específica. La idea es analizar un archivo de configuración creado por el usuario y a partir de las definiciones generar una familia de objetos que se pueden llamar como simples objetos python, pero bajo el capó implementar una interfaz de almacenamiento en búfer con una base de datos, un poco como SQLAlchemy, pero sin la necesidad escribir todas las asignaciones y el código de definición de objeto explícitamente. – chiffa

1

El error parece ser bastante obvio para mí:

SyntaxError: unqualified exec is not allowed in function 'test2' it contains a nested function with free variables 

Ver pep 227 para obtener más información: http://www.python.org/dev/peps/pep-0227/

4

que funciona bien en Python 3.1.3, después de modificar la declaración de impresión para utilizar la impresión función.

En Python 2.6, produce SyntaxError: unqualified exec is not allowed in function 'test2' it contains a nested function with free variables, no creo que sea un error.

24

Aunque en Python se ve algo así como las variables locales se almacenan en un diccionario locals(), que por lo general no lo son. En su lugar, se almacenan principalmente en la pila y se accede por índice. Esto hace que la búsqueda de variables locales sea más rápida que si tuviera que buscar un diccionario cada vez. Si usa la función locals(), entonces lo que obtiene es un diccionario nuevo creado a partir de todas las variables locales, y es por eso que la asignación a locals() generalmente no funciona.

Hay un par de excepciones a este escenario:

Cuando se utiliza un rotundo exec dentro de una función de Python se apaga la optimización y utiliza un diccionario real para las variables locales. Eso significa que puede crear o actualizar variables desde el interior del exec, pero también significa que todo acceso de variable local en esa función se ejecutará más lentamente.

La otra excepción es que cuando anida funciones, la función interna puede acceder a las variables locales en el ámbito de la función externa.Cuando lo hace, la variable se almacena en un objeto 'celda' en lugar de almacenarse en la pila. El nivel adicional de direccionamiento indirecto hace que todo el uso de las variables delimitadas sea más lento ya sea que acceda a ellos desde la función interna o externa.

El truco que ha encontrado es que estas dos excepciones a cómo se almacenan normalmente las variables locales son incompatibles. No puede tener una variable almacenada en un diccionario y acceder a ella a través de una referencia de celda al mismo tiempo. Python 2.x lo soluciona al no permitir el uso del exec, incluso en casos como este en los que no intenta utilizar ninguna variable con ámbito.

2

Este es un caso bastante interesante:

>>> def func(): 
...  exec('print "hi from func"') 
...  def subfunction(): 
...   return True 
... 
    File "<stdin>", line 2 
SyntaxError: unqualified exec is not allowed in function 'func' because 
it contains a nested function with free variables 

La razón por la que esto no funciona de hecho es que subfunction contiene una variable libre, y dado que en Python 2, exec teóricamente podría modificar la gente del lugar en el que contiene alcance, sería imposible decidir si la variable debería estar vinculada al alcance de la función global o principal. Uno de los versos en el Zen de Python es "En vista de la ambigüedad, rechace la tentación de adivinar". y esto es lo que hace Python 2.

Ahora la pregunta es: ¿qué es esta variable independiente? Bueno, es el True!

De hecho, es reproducible con None:

>>> def func(): 
...  exec('print "hi from func"') 
...  def subfunction(): 
...   return None 
... 
    File "<stdin>", line 2 
SyntaxError: unqualified exec is not allowed in function 'test2' because it contains a nested 
function with free variables 

Aunque None no se pueden asignar a, y es considerado como un constante en el código de bytes, el analizador cochecito piensa que es una variable no unido aquí.

Pero si que se sustituya por 1 y funciona sin problemas:

>>> def test2(): 
...  exec('print "hi from func"') 
...  def subfunction(): 
...   return 1 
... 
>>> 

Para evitar este error, explicitará las variables globales y posiblemente lugareños que van a ser utilizados por exec, di:

>>> def test2(): 
...  exec 'print "hi from test2"' in {} 
...  def subfunction(): 
...   return None 
... 
>>> 

En Python 3, exec es simplemente una función simple y no es manejada especialmente por el analizador o el compilador de códigos de bytes. En Python 3 exec no se pueden volver a enlazar los nombres locales de la función, y por lo tanto este SyntaxError y la ambigüedad no existen.


Un caso peculiar en Python 2 vs 3 compatibilidad es que mientras Python 2.7 documentation establece que

La forma exec(expr, globals) es equivalente a exec expr in globals, mientras que la forma exec(expr, globals, locals) es equivalente a exec expr in globals, locals. La forma de tupla de exec proporciona compatibilidad con Python 3, donde exec es una función en lugar de una declaración.

La forma de tupla no siempre ha sido 100% compatible, ya que hubo a bug in handling of exec in functions with nested functions (issue 21591); hasta Python 2.7.8 el siguiente código podría haber arrojado una excepción:

def func(): 
    exec('print "hi from test2"', {}) 
    def subfunction(): 
     return None 

Esto se arregló en Python 2.7.9 y ya no arroja.

+1

Para evitar la lamentable situación de Python2/Python3 * exec * bugginess y compatibilidad, también puede usar el modismo 'edict = {}; eval (compilar (instrucción, '', 'exec'), globals(), edicto) '. Esto da acceso a los efectos secundarios de * exec * (vea también [esta discusión de exec/eval/compile] (http://stackoverflow.com/a/29456463/2127439)). – wolfmanx

Cuestiones relacionadas