Возможная ошибка в модуле 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.

  • Изучение модуля Queue в python (как его запустить)
  • Python, ctypes, многомерный массив
  • 3 Solutions collect form web for “Возможная ошибка в модуле pdb в Python 3 при использовании генераторов списков”

    если вы набираете текст в сеансе [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глобальная переменная. Заметим, что .0 , аргумент-аргумент списка, является итерабельностью, полученной из seq , а не seq . (см. код операции GET_ITER в выводе выше). Это более понятно с более сложным примером:

     >>> 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 и сами не передаются в список. Внутри списка понимается только 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 , потребует значительных изменений в том, как обрабатываются функции, и я не знаю, можно ли это сделать без изменения интерпретатора.


    Обратите внимание, что при использовании вложенных списков-вложений вложенный цикл расширяется в байт-коде, например, в 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] – я бы предположил, что вам нужно назначить локальный сначала скопируйте, прежде чем пытаться это сделать.

    Python - лучший язык программирования в мире.