9
class Some(object):
    tokens = [ ... list of strings ... ]
    untokenized = [tokens.index(a) for a in [... some other list of strings ...]]
    ... etc ...
some = Some()

これは Python2.7 で問題なく動作します。ただし、python3 は次のように述べています。

Traceback (most recent call last):
File "./test.py", line 17, in <module>
    class Some(object):
File "./test.py", line 42, in Some
    untokenized = [tokens.index(a) for a in [... some other list of strings ...]]
File "./test.py", line 42, in <listcomp>
    untokenized = [tokens.index(a) for a in [... some other list of strings ...]]
NameError: global name 'tokens' is not defined

問題を回避することはできますが、Python2 と Python3 の違いを知りたいです。Python 2->3 の変更に関するドキュメントを読みましたが、問題に関連する説明を特定できませんでした。また2to3、ツールは私のコードで何も文句を言いません。

ちなみに、今は状況を思い出せませんが、python2だけでも似たようなことがありました(3でも試していません)が、これは(クラス内で)うまくいくはずだと思いました:

def some_method(self):
    return {a: eval("self." + a) for a in dir(self) if not a.startswith("_")}

ただし、python2 は次のように言っています:NameError: name 'self' is not defined 私はまだ python3 でこれを試していませんが、たとえば、これは機能します:

[eval("self." + a) for a in dir(self) if not a.startswith("_")]

前の例の関連部分をこれに変更すると (例自体は少しばかげていますが、少なくとも私の問題を示しています)。今、私は非常に興味があります.なぜselfこの最初の例では定義されていないようですが、2番目の例では定義されているのですか? 辞書のようですが、元の質問と同様の問題がありますが、リストジェネレータ式では機能しますが、python3では機能しません。うーん...

私の python2 -> 3 問題の後、私はこれについて言及しました。これらはすべて、Python インタープリターに従って何かが定義されていないという問題に関するものであるように思われるからです (そして、私の質問の 2 番目の部分は無関係ではないでしょうか?)。私は今かなり混乱しています。私の間違いについて教えてください(もちろん、何かを逃したと確信しているので)。

4

1 に答える 1

14

Wooble が言うように、問題はクラスにレキシカル スコープがないことです (実際には、Python 2またはPython 3のいずれか)。代わりに、スコープを構成しないローカル名前空間があります。これは、クラス定義内の式が名前空間のコンテンツにアクセスできることを意味します。

class C:
    a = 2
    b = a + 2    # b = 4

ただし、クラスの本体内に導入されたスコープは、その名前空間にアクセスできません。

class C:
    a = 2
    def foo(self):
        return a    # NameError: name 'a' is not defined, use return self.__class__.a

Python 2 と Python 3 の違いは、Python 2 ではリスト内包表記が新しいスコープを導入しないことです。

[a for a in range(3)]
print a    # prints 2

一方、Python 3 では次のようにします。

[a for a in range(3)]
print(a)    # NameError: name 'a' is not defined

これは、Python 3 でいくつかの理由で変更されました。(a for a in range(3))Python 2 と Python 3 の両方に独自のスコープがあります。

そのため、クラスの本体内で、Python 2 genexp または Python 3 listcomp または genexp は新しいスコープを導入するため、クラス定義のローカル名前空間にアクセスできません。

genexp/listcomp がクラス定義名前空間から名前にアクセスできるようにする方法は、関数またはラムダを使用して新しいスコープを導入することです。

class C:
    a = 2
    b = (lambda a=a: [a + i for i in range(3)])()


eval問題_

あなたのeval例の問題はeval、デフォルトではローカルスコープで引数を評価することです。Python 2 のリスト内包表記には、囲んでいるスコープを共有する上記の動作がありeval、メソッド スコープにアクセスできますが、genexp または Python 3 の listcomp ローカル スコープには、囲んでいるスコープから必要であるとコンパイラが判断できるものしかありません (genexp/listcomp スコープであるため)。は閉鎖です):

def bar(x):
    return list(eval('x') + x for i in range(3))
bar(5)  # returns [10, 10, 10]

def baz(x):
    return list(eval('x') for i in range(3))
baz(5)  # NameError: name 'x' is not defined

Martijn が言うように、代わりにevalを使用する必要がありますgetattr

于 2012-07-26T13:22:54.083 に答える