Возможная ошибка в модуле pdb в Python 3 при использовании генераторов списков

После запуска этого кода в 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.

Ответ 1

если вы наберете interact в своем сеансе [i] pdb, вы получите интерактивный сеанс, и в этом режиме функции поиска будут работать как ожидалось.

источник: http://bugs.python.org/msg215963

Ответ 2

Он отлично работает:

>>> 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 прошло успешно).

Ответ 3

Я просто не понимаю, почему вам нужно будет сделать выше, если вы хотите создать список Trues для каждого элемента в seq, тогда почему бы не [True for x in seq] - я бы предположил, что вам нужно сначала назначьте локальную копию, прежде чем пытаться это сделать.