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工具不抱怨在我的代码什么。
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尝试过这个,但是例如,这可行:
NameError: name 'self' is not defined
[eval("self." + a) for a in dir(self) if not a.startswith("_")]
如果我将上一个示例的相关部分更改为此示例(好的示例本身有点愚蠢,但至少显示了我的问题)。现在我很好奇,为什么self似乎没有为第一个示例定义而是为第二个示例定义?看起来像字典一样,我也有类似的问题,这是我原来的问题,但是列表生成器表达式可以工作,但在python3中却不行。嗯…
self
在我的python2-> 3问题之后,我提到了这一点,因为所有这些似乎都与以下问题有关:根据python解释器未定义某些内容(也许我问题的第二部分是不相关的?)。我现在感到很困惑。请给我有关我的错误的启发(因为我确信我当然错过了某些事情)。
正如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中对此进行了更改,其原因有两个,包括使列表推导的行为与生成器表达式(genexps)相同。(a for a in range(3))在Python 2和Python 3中都有自己的作用域。
(a for a in range(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)])()
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。
getattr