python - chart - title matplot
Posible error en el módulo pdb en Python 3 al usar generadores de listas (3)
Funciona perfectamente bien:
>>> import pdb
>>> def f(seq):
... pdb.set_trace()
...
>>> f([1,2,3])
--Return--
> <stdin>(2)f()->None
(Pdb) [x for x in seq]
[1, 2, 3]
(Pdb) [x in seq for x in seq]
[True, True, True]
Sin mostrar lo que realmente está haciendo, nadie puede decirle por qué en su caso específico obtuvo un NameError
.
TL; DR en python3 list-comprensión son en realidad funciones con su propio marco de pila, y no se puede acceder a la variable seq
, que es un argumento de test
, desde los marcos de pila internos. En su lugar, se trata como un global (y, por lo tanto, no se encuentra).
Lo que ves es la implementación diferente de list-comprensión en python2 vs python3. En Python 2, la lista de comprensión es en realidad una mano corta para el bucle for
, y puedes ver esto claramente en el código de bytes:
>>> def test(): [x in seq for x in seq]
...
>>> dis.dis(test)
1 0 BUILD_LIST 0
3 LOAD_GLOBAL 0 (seq)
6 GET_ITER
>> 7 FOR_ITER 18 (to 28)
10 STORE_FAST 0 (x)
13 LOAD_FAST 0 (x)
16 LOAD_GLOBAL 0 (seq)
19 COMPARE_OP 6 (in)
22 LIST_APPEND 2
25 JUMP_ABSOLUTE 7
>> 28 POP_TOP
29 LOAD_CONST 0 (None)
32 RETURN_VALUE
Observe cómo el FOR_ITER
contiene un bucle FOR_ITER
. Por otro lado, en python3 list-comprensión son en realidad funciones con su propio marco de pila:
>>> def test(): [x in seq2 for x in seq]
...
>>> dis.dis(test)
1 0 LOAD_CONST 1 (<code object <listcomp> at 0xb6fef160, file "<stdin>", line 1>)
3 MAKE_FUNCTION 0
6 LOAD_GLOBAL 0 (seq)
9 GET_ITER
10 CALL_FUNCTION 1
13 POP_TOP
14 LOAD_CONST 0 (None)
17 RETURN_VALUE
Como puede ver, aquí no hay FOR_ITER
, en su lugar hay un bytecodes MAKE_FUNCTION
y CALL_FUNCTION
. Si examinamos el código de la lista-comprensión podemos entender cómo se configuran los enlaces:
>>> test.__code__.co_consts[1]
<code object <listcomp> at 0xb6fef160, file "<stdin>", line 1>
>>> test.__code__.co_consts[1].co_argcount # it has one argument
1
>>> test.__code__.co_consts[1].co_names # global variables
(''seq2'',)
>>> test.__code__.co_consts[1].co_varnames # local variables
(''.0'', ''x'')
Aquí .0
es el único argumento de la función. x
es la variable local del bucle y seq2
es una variable global . Tenga en cuenta que .0
, el argumento de comprensión de lista, es el iterable obtenido de seq
, no seq
. (Ver el código de operación GET_ITER
en la salida de dis
arriba). Esto es más claro con un ejemplo más complejo:
>>> def test():
... [x in seq for x in zip(seq, a)]
...
>>> dis.dis(test)
2 0 LOAD_CONST 1 (<code object <listcomp> at 0xb7196f70, file "<stdin>", line 2>)
3 MAKE_FUNCTION 0
6 LOAD_GLOBAL 0 (zip)
9 LOAD_GLOBAL 1 (seq)
12 LOAD_GLOBAL 2 (a)
15 CALL_FUNCTION 2
18 GET_ITER
19 CALL_FUNCTION 1
22 POP_TOP
23 LOAD_CONST 0 (None)
26 RETURN_VALUE
>>> test.__code__.co_consts[1].co_varnames
(''.0'', ''x'')
Aquí puede ver que el único argumento para la comprensión de lista, siempre denotado por .0
, es el iterable obtenido de zip(seq, a)
. seq
y a
sí mismos no se pasan a la lista-comprensión. Solo se pasa iter(zip(seq, a))
dentro de la comprensión de la lista.
Otra observación que debemos hacer es que, cuando ejecuta pdb
, no puede acceder al contexto de la función actual desde las funciones que desea definir. Por ejemplo, el siguiente código falla tanto en python2 como en python3:
>>> import pdb
>>> def test(seq): pdb.set_trace()
...
>>> test([1,2,3])
--Return--
> <stdin>(1)test()->None
(Pdb) def test2(): print(seq)
(Pdb) test2()
*** NameError: global name ''seq'' is not defined
Falla porque al definir test2
la variable seq
se trata como una variable global , pero en realidad es una variable local dentro de la función de test
, por lo que no es accesible.
El comportamiento que ves es similar al siguiente escenario:
#python 2 no error
>>> class A(object):
... x = 1
... L = [x for _ in range(3)]
...
>>>
#python3 error!
>>> class A(object):
... x = 1
... L = [x for _ in range(3)]
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in A
File "<stdin>", line 3, in <listcomp>
NameError: global name ''x'' is not defined
El primero no da un error porque es mayormente equivalente a:
>>> class A(object):
... x = 1
... L = []
... for _ in range(3): L.append(x)
...
Dado que la lista de comprensión está "expandida" en el bytecode. En python3 falla porque en realidad estás definiendo una función y no puedes acceder al alcance de la clase desde un alcance de función anidado:
>>> class A(object):
... x = 1
... def test():
... print(x)
... test()
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in A
File "<stdin>", line 4, in test
NameError: global name ''x'' is not defined
Tenga en cuenta que los genexp se implementan como funciones en python2, y de hecho se ve un comportamiento similar con ellos (tanto en python2 como en python3):
>>> import pdb
>>> def test(seq): pdb.set_trace()
...
>>> test([1,2,3])
--Return--
> <stdin>(1)test()->None
(Pdb) list(x in seq for x in seq)
*** Error in argument: ''(x in seq for x in seq)''
Aquí pdb
no le da más detalles, pero la falla ocurre por la misma razón.
En conclusión: no es un error en pdb
sino la forma en que Python implementa los ámbitos. AFAIK cambiar esto para permitir lo que está intentando hacer en pdb
requeriría algunos cambios importantes en la forma en que se tratan las funciones y no sé si esto se puede hacer sin modificar el intérprete.
Tenga en cuenta que al usar listas-comprensión anidadas, el bucle anidado se expande en código de bytes como las listas-comprensión en python2:
>>> import dis
>>> def test(): [x + y for x in seq1 for y in seq2]
...
>>> dis.dis(test)
1 0 LOAD_CONST 1 (<code object <listcomp> at 0xb71bf5c0, file "<stdin>", line 1>)
3 MAKE_FUNCTION 0
6 LOAD_GLOBAL 0 (seq1)
9 GET_ITER
10 CALL_FUNCTION 1
13 POP_TOP
14 LOAD_CONST 0 (None)
17 RETURN_VALUE
>>> # The only argument to the listcomp is seq1
>>> import types
>>> func = types.FunctionType(test.__code__.co_consts[1], globals())
>>> dis.dis(func)
1 0 BUILD_LIST 0
3 LOAD_FAST 0 (.0)
>> 6 FOR_ITER 29 (to 38)
9 STORE_FAST 1 (x)
12 LOAD_GLOBAL 0 (seq2)
15 GET_ITER
>> 16 FOR_ITER 16 (to 35)
19 STORE_FAST 2 (y)
22 LOAD_FAST 1 (x)
25 LOAD_FAST 2 (y)
28 BINARY_ADD
29 LIST_APPEND 3
32 JUMP_ABSOLUTE 16
>> 35 JUMP_ABSOLUTE 6
>> 38 RETURN_VALUE
Como puede ver, el bytecode para listcomp
tiene un FOR_ITER
explícito sobre seq2
. Este FOR_ITER
explícito está dentro de la función listcomp y, por lo tanto, las restricciones sobre los ámbitos aún se aplican (por ejemplo, seq2
se carga como un global).
Y de hecho podemos confirmar esto usando pdb
:
>>> import pdb
>>> def test(seq1, seq2): pdb.set_trace()
...
>>> test([1,2,3], [4,5,6])
--Return--
> <stdin>(1)test()->None
(Pdb) [x + y for x in seq1 for y in seq2]
*** NameError: global name ''seq2'' is not defined
(Pdb) [x + y for x in non_existent for y in seq2]
*** NameError: name ''non_existent'' is not defined
Observe cómo el NameError
se trata de seq2
y no seq1
(que se pasa como argumento de la función), y observe cómo el cambio del primer nombre iterable a algo que no existe cambia el NameError
(lo que significa que en el primer caso se pasó con éxito) seq1
) .
Después de ejecutar este código en Python 3:
import pdb
def foo():
nums = [1, 2, 3]
a = 5
pdb.set_trace()
foo()
Las siguientes expresiones funcionan:
(Pdb) print(nums)
[1, 2, 3]
(Pdb) print(a)
5
(Pdb) [x for x in nums]
[1, 2, 3]
pero la siguiente expresión falla:
(Pdb) [x*a for x in nums]
*** NameError: global name ''a'' is not defined
Lo anterior funciona bien en Python 2.7.
¿Es esto un error o me falta algo?
Actualización : ver la nueva respuesta aceptada. Esto fue realmente un error (o un diseño problemático) que se ha abordado ahora mediante la introducción de un nuevo comando y modo en pdb.
Simplemente no puedo entender por qué tendría que hacer lo anterior si está buscando producir una lista de Trues para cada elemento en seq, entonces por qué no [Verdadero para x en seq] - Supongo que necesita asignar un local Copia primero antes de probar este tipo de cosas.
si escribe interact
en su sesión de [i] pdb, obtiene una sesión interactiva y las comprensiones de lista funcionan como se espera en este modo
fuente: http://bugs.python.org/msg215963