使用python3的未定义的全局列表生成器表达式,与python2一起使用,需要进行哪些更改?

LGB*_*LGB 9 python python-3.x

class Some(object):
    tokens = [ ... list of strings ... ]
    untokenized = [tokens.index(a) for a in [... some other list of strings ...]]
    ... etc ...
some = Some()
Run Code Online (Sandbox Code Playgroud)

这适用于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
Run Code Online (Sandbox Code Playgroud)

虽然我可以解决这个问题,但我真的想知道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("_")}
Run Code Online (Sandbox Code Playgroud)

然而它导致python2说:NameError: name 'self' is not defined 我还没有用python3尝试过这个,但是例如这有效:

[eval("self." + a) for a in dir(self) if not a.startswith("_")]
Run Code Online (Sandbox Code Playgroud)

如果我将前一个示例的相关部分更改为此示例(确定示例本身有点愚蠢,但它至少显示我的问题).现在我很好奇,为什么self似乎没有为第一个例子定义,但它是第二个?看来,我有类似的问题,我的原始问题是关于,但使用列表生成器表达它可以工作,但不是在python3.嗯......

在我的python2 - > 3问题之后我提到了这一点,因为所有这些似乎都是关于根据python解释器没有定义某些东西的问题(也许我的问题的第二部分是无关的?).我现在感到很困惑.请告诉我我的错误(因为我确信我当然错过了一些东西).

eca*_*mur 14

正如Wooble所说,问题是类没有词法范围(实际上,在Python 2Python 3中).相反,它们具有不构成范围的本地命名空间.这意味着类定义中的表达式可以访问命名空间的内容:

class C:
    a = 2
    b = a + 2    # b = 4
Run Code Online (Sandbox Code Playgroud)

但是在类体内引入的范围无法访问其命名空间:

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

Python 2和Python 3之间的区别在于,在Python 2中,列表推导不会引入新的范围:

[a for a in range(3)]
print a    # prints 2
Run Code Online (Sandbox Code Playgroud)

而在Python 3中他们做:

[a for a in range(3)]
print(a)    # NameError: name 'a' is not defined
Run Code Online (Sandbox Code Playgroud)

这在Python 3中有所改变,原因有两个,包括使列表推导的行为与generator-expressions(genexps)相同; (a for a in range(3))在Python 2和Python 3中都有自己的范围.

因此,在类的主体内,Python 2 genexp或Python 3 listcomp或genexp引入了新的作用域,因此无法访问类定义本地名称空间.

使genexp/listcomp访问类定义命名空间中的名称的方法是使用函数或lambda引入新范围:

class C:
    a = 2
    b = (lambda a=a: [a + i for i in range(3)])()
Run Code Online (Sandbox Code Playgroud)


这个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
Run Code Online (Sandbox Code Playgroud)

正如Martijn所说,而不是eval你应该使用getattr.