Ответ 1
если вы наберете interact
в своем сеансе [i] pdb, вы получите интерактивный сеанс, и в этом режиме функции поиска будут работать как ожидалось.
источник: http://bugs.python.org/msg215963
После запуска этого кода в Python 3:
import pdb
def foo():
nums = [1, 2, 3]
a = 5
pdb.set_trace()
foo()
Работают следующие выражения:
(Pdb) print(nums)
[1, 2, 3]
(Pdb) print(a)
5
(Pdb) [x for x in nums]
[1, 2, 3]
но следующее выражение не выполняется:
(Pdb) [x*a for x in nums]
*** NameError: global name 'a' is not defined
Вышеописанное отлично работает в Python 2.7.
Является ли это ошибкой или что-то не хватает?
Обновить. См. новый принятый ответ. Это действительно ошибка (или проблемный дизайн), который был рассмотрен сейчас, введя новую команду и режим в pdb.
если вы наберете interact
в своем сеансе [i] pdb, вы получите интерактивный сеанс, и в этом режиме функции поиска будут работать как ожидалось.
источник: http://bugs.python.org/msg215963
Он отлично работает:
>>> 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]
Не показывая, что вы на самом деле делаете, никто не может сказать вам, почему в вашем конкретном случае вы получили NameError
.
TL; DR. В python3 перечисления-списки - это фактически функции со своим собственным стековым фреймом, и вы не можете получить доступ к переменной seq
, которая является аргументом test
, из внутренних стековых фреймов, Вместо этого он рассматривается как глобальный (и, следовательно, не найден).
То, что вы видите, - это различная реализация понимания списков в python2 vs python3.
В python 2 -понимание понятий на самом деле является короткой рукой для цикла for
, и вы можете четко видеть это в байт-коде:
>>> 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
Обратите внимание, как байт-код содержит цикл FOR_ITER
. С другой стороны, в python3 использование списка - это фактически функции со своим собственным фреймом стека:
>>> 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
Как вы можете видеть, здесь нет FOR_ITER
, вместо этого есть байт-коды MAKE_FUNCTION
и CALL_FUNCTION
. Если мы рассмотрим код понимания списка, мы сможем понять, как устанавливаются привязки:
>>> 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')
Здесь .0
- единственный аргумент функции. x
является локальной переменной цикла, а seq2
является переменной global. Обратите внимание, что .0
, аргумент-аргумент списка, является итерабельностью, полученной из seq
, а не seq
. (см. код операции GET_ITER
на выходе dis
выше). Это более понятно с более сложным примером:
>>> 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')
Здесь вы можете видеть, что единственным аргументом в понимании списка, всегда обозначаемым .0
, является итерабельность, полученная из zip(seq, a)
. seq
и a
сами не передаются в список. Только iter(zip(seq, a))
передается внутри понимания списка.
Другое замечание, которое мы должны сделать, заключается в том, что при запуске pdb
вы не можете получить доступ к контексту текущей функции из функций, которые вы хотите определить. Например, следующий код не работает как на python2, так и на 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
Это терпит неудачу, потому что при определении test2
переменная seq
рассматривается как глобальная переменная, но она фактически является локальной переменной внутри функции test
, поэтому она недоступна.
Поведение, которое вы видите, похоже на следующий сценарий:
#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
Первый не дает ошибки, поскольку он в основном эквивалентен:
>>> class A(object):
... x = 1
... L = []
... for _ in range(3): L.append(x)
...
Так как в байт-коде понимается "раскрытие" списка. В python3 он терпит неудачу, потому что вы фактически определяете функцию, и вы не можете получить доступ к области класса из области вложенных функций:
>>> 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
Обратите внимание, что genexp реализованы как функции на python2, и на самом деле вы видите с ними сходное поведение (как на python2, так и на 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)'
Здесь pdb
не дает вам более подробной информации, но сбой происходит по той же самой причине.
В заключение: это не ошибка в pdb
, а способ, которым python реализует области. AFAIK, изменяя это, чтобы разрешить то, что вы пытаетесь сделать в pdb
, потребует значительных изменений в том, как обрабатываются функции, и я не знаю, можно ли это сделать без изменения интерпретатора.
Обратите внимание, что при использовании вложенных list-comprehensions вложенный цикл расширяется в байт-коде, например, в 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
Как вы можете видеть, байт-код для listcomp
имеет явную FOR_ITER
над seq2
.
Этот явный FOR_ITER
находится внутри функции listcomp, и, следовательно, ограничения по областям все еще применяются (например, seq2
загружается как глобальный).
И на самом деле мы можем подтвердить это, используя 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
Обратите внимание, что NameError
около seq2
, а не seq1
(который передается как аргумент функции), и обратите внимание, как изменение первого имени итерируемого на то, что не существует, изменяет NameError
(который означает, что в первом случае seq1
прошло успешно).
Я просто не понимаю, почему вам нужно будет сделать выше, если вы хотите создать список Trues для каждого элемента в seq, тогда почему бы не [True for x in seq] - я бы предположил, что вам нужно сначала назначьте локальную копию, прежде чем пытаться это сделать.