25

このコードを 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 に新しいコマンドとモードを導入することで対処されています。

4

3 に答える 3

25

interact[i]pdb セッションを入力すると、インタラクティブなセッションが開始され、このモードではリスト内包表記が期待どおりに機能します

ソース: http://bugs.python.org/msg215963

于 2016-06-03T22:48:46.497 に答える
10

それは完全にうまくいきます:

>>> 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 と 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ここにはありませんが、代わりに aMAKE_FUNCTIONCALL_FUNCTIONbytecodes があります。リスト内包表記のコードを調べると、バインディングがどのように設定されているかを理解できます。

>>> 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から取得した iterable であることに注意してください。(上記の出力のオペコードを参照してください)。これは、より複雑な例でより明確になります。seqseqGET_ITERdis

>>> 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ここで、常に で示されるリスト内包表記の唯一の引数は、 から取得した iterableであることがわかりますzip(seq, a)seqそしてaそれ自体はリスト内包表記に渡されません。リスト内包内でのみiter(zip(seq, a))渡されます。

もう 1 つ確認しなければならない点は、 を実行する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 がスコープを実装する方法です。私の知る限り、これを変更してあなたがやろうとしていることを許可するには、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        

ご覧のとおり、バイトコード forlistcompには明示的なFOR_ITERover があり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

The NameErroris aboutseq2と not seq1(関数の引数として渡される) に注意し、最初の iterable 名を存在しないものに変更すると がどのように変更されるかに注意してくださいNameError(つまり、最初のケースseq1では正常に渡されたことを意味します)。

于 2013-06-25T06:34:43.410 に答える