Undefined global en la expresión del generador de listas usando python3, funciona con python2, ¿qué cambios son necesarios?

class Some(object): tokens = [ ... list of strings ... ] untokenized = [tokens.index(a) for a in [... some other list of strings ...]] ... etc ... some = Some() 

Esto funciona bien con Python2.7. Sin embargo python3 dice:

 Traceback (most recent call last): File "./test.py", line 17, in  class Some(object): File "./test.py", line 42, in Some untokenized = [tokens.index(a) for a in [... some other list of strings ...]] File "./test.py", line 42, in  untokenized = [tokens.index(a) for a in [... some other list of strings ...]] NameError: global name 'tokens' is not defined 

Aunque puedo solucionar el problema, me gustaría saber cuál es la diferencia entre Python2 y Python3. He leído Python 2-> 3 documentos de cambios, pero no pude identificar ninguna descripción relacionada con mi problema. También la herramienta 2to3 no se queja de nada en mi código.

Por cierto, aunque no puedo recordar la situación ahora, pero también tuve algo similar con python2 (ni siquiera he intentado esto con 3), pensé que debería funcionar (dentro de una clase):

 def some_method(self): return {a: eval("self." + a) for a in dir(self) if not a.startswith("_")} 

Sin embargo, hace que Python2 diga: NameError: name 'self' is not defined Todavía no he probado esto con python3, pero por ejemplo, esto funciona:

 [eval("self." + a) for a in dir(self) if not a.startswith("_")] 

Si cambio la parte relevante del ejemplo anterior a esta (bueno, el ejemplo en sí es un poco estúpido, pero al menos muestra mi problema). Ahora estoy muy curioso, ¿por qué el self parece no estar definido para este primer ejemplo, pero es para el segundo? Parece que con los dictados, tengo un problema similar del que trata mi pregunta original, pero con la expresión del generador de listas funciona, pero no en python3. Hmmm …

Después de mi problema con python2 -> 3 mencioné esto, ya que todo esto parece ser sobre el problema de que algo no está definido de acuerdo con el intérprete de python (¿y tal vez la segunda parte de mi pregunta no esté relacionada?). Me siento bastante confundido ahora. Por favor, ilumíneme sobre mi error (ya que estoy seguro de que me perdí algo, por supuesto).

Related of "Undefined global en la expresión del generador de listas usando python3, funciona con python2, ¿qué cambios son necesarios?"

Como dice Wooble, el problema es que las clases no tienen un scope léxico (en realidad, ya sea en Python 2 o Python 3 ). En su lugar, tienen un espacio de nombres local que no constituye un scope. Esto significa que las expresiones dentro de la definición de clase tienen acceso al contenido del espacio de nombres:

 class C: a = 2 b = a + 2 # b = 4 

pero los ámbitos introducidos dentro del cuerpo de la clase no tienen acceso a su espacio de nombres:

 class C: a = 2 def foo(self): return a # NameError: name 'a' is not defined, use return self.__class__.a 

La diferencia entre Python 2 y Python 3 es que en Python 2, la comprensión de las listas no introduce un nuevo scope:

 [a for a in range(3)] print a # prints 2 

Mientras que en Python 3 hacen:

 [a for a in range(3)] print(a) # NameError: name 'a' is not defined 

Esto se cambió en Python 3 por un par de razones, incluso para hacer que las comprensiones de listas se comporten de la misma manera que las expresiones-generador (genexps); (a for a in range(3)) tiene su propio scope tanto en Python 2 como en Python 3.

Por lo tanto, dentro del cuerpo de una clase, un genexp de Python 2 o una lista o genexp de Python 3 introduce un nuevo scope y, por lo tanto, no tiene acceso al espacio de nombres local de definición de clase.

La forma de otorgar a genexp / listcomp acceso a los nombres del espacio de nombres de definición de clase es introducir un nuevo ámbito, usando una función o un lambda:

 class C: a = 2 b = (lambda a=a: [a + i for i in range(3)])() 

El problema eval

El problema con su ejemplo de eval es que eval evalúa de forma predeterminada su argumento en el ámbito local; debido a que las comprensiones de la lista de Python 2 tienen el comportamiento anterior de compartir el scope adjunto, el eval puede acceder al scope del método, pero un scope local genexp o Python 3 listcomp solo tiene lo que el comstackdor puede decir que se requiere del scope adjunto (desde genexp / listcomp el scope es un cierre):

 def bar(x): return list(eval('x') + x for i in range(3)) bar(5) # returns [10, 10, 10] def baz(x): return list(eval('x') for i in range(3)) baz(5) # NameError: name 'x' is not defined 

Como dice Martijn, en lugar de eval debes usar getattr .