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似乎没有为第一个示例定义,而是为第二个示例定义?似乎对于 dicts,我有类似的问题,我的原始问题是关于的,但是使用列表生成器表达式它可以工作,但在 python3 中不起作用。嗯...

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

4

1 回答 1

14

正如 Wooble 所说,问题在于类没有词法范围(实际上,在Python 2Python 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 中都有自己的范围。

因此,在类的主体中,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.

于 2012-07-26T13:22:54.083 回答