Исправлена ​​ошибка с просмотром области видимости от отладчика Python

В отладке моего кода я хочу использовать понимание списка. Однако, кажется, я не могу оценить понимание списка из отладчика, когда я внутри функции.

Я использую Python 3.4.

Script содержимое:

$ cat test.py 
#!/usr/bin/python

def foo():
    x = [1, 2, 3, 3, 4]

    print(x)

foo()

Интерактивная отладка:

$ python3 -mpdb test.py                                                                                                                                           
> /tmp/test.py(3)<module>()
-> def foo():
(Pdb) step
> /tmp/test.py(8)<module>()
-> foo()
(Pdb) 
--Call--
> /tmp/test.py(3)foo()
-> def foo():
(Pdb) 
> /tmp/test.py(4)foo()
-> x = [1, 2, 3, 3, 4]
(Pdb) 
> /tmp/test.py(6)foo()
-> print(x)
(Pdb) p [x for _ in range(1)]
*** NameError: name 'x' is not defined
(Pdb) p x
[1, 2, 3, 3, 4]

Почему x неизвестно пониманию списка? Как я могу оценить понимание списка из отладчика или добиться эквивалентного поведения? Это ошибка, или это какое-то фундаментальное ограничение для отладчика?

Ответ 1

В Python 3 вы должны использовать команду interact в pdb, прежде чем сможете получить доступ к любым неглобальным переменным из-за изменения способа реализации понятий.

>>> def foo(): [][0]
... 
>>> foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in foo
IndexError: list index out of range
>>> import pdb;pdb.pm()
> <stdin>(1)foo()
(Pdb) x = 4
(Pdb) [x for _ in range(2)]
*** NameError: name 'x' is not defined
(Pdb) interact
*interactive*
>>> [x for _ in range(2)]
[4, 4]
>>> 

Ответ 2

pdb, похоже, работает с кодом:

eval(compiled_code, globals(), locals())

(или, может быть, даже просто eval(string, globals(), locals())).

К сожалению, при компиляции Python не знает локальных переменных. Это обычно не имеет значения:

import dis
dis.dis(compile("x", "", "eval"))
#>>>   1           0 LOAD_NAME                0 (x)
#>>>               3 RETURN_VALUE

но когда вводится другая область, например, с пониманием списка lambda, это плохо компилируется:

dis.dis(compile("(lambda: x)()", "", "eval"))
#>>>   1           0 LOAD_CONST               0 (<code object <lambda> at 0x7fac20708d20, file "", line 1>)
#>>>               3 LOAD_CONST               1 ('<lambda>')
#>>>               6 MAKE_FUNCTION            0
#>>>               9 CALL_FUNCTION            0 (0 positional, 0 keyword pair)
#>>>              12 RETURN_VALUE
# The code of the internal lambda
dis.dis(compile("(lambda: x)()", "", "eval").co_consts[0])
#>>>   1           0 LOAD_GLOBAL              0 (x)
#>>>               3 RETURN_VALUE

Обратите внимание, что a LOAD_GLOBAL, где x находится в локальной области.


Здесь совершенно глупый взломать его:

(Pdb) eval("(lambda: x)()", vars())
[1, 2, 3, 3, 4]