使用 python3 的列表生成器表达式中的未定义全局,与 python2 一起使用,需要进行哪些更改? [英] Undefined global in list generator expression using python3, works with python2, what changes are needed?

查看:27
本文介绍了使用 python3 的列表生成器表达式中的未定义全局,与 python2 一起使用,需要进行哪些更改?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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说:

This works fine with Python2.7. However python3 says:

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 工具不会在我的代码中抱怨任何内容.

Though I can work around the problem, I really would like to know what's difference here between Python2 and Python3. I've read python 2->3 changes documents, but I was not able to identify any description which is related to my problem. Also 2to3 tool does not complain anything in my code.

顺便说一句,虽然我现在不记得情况了,但是我也有类似的东西 only python2 (我什至没有用 3 尝试过),我认为这应该可行(在一个班级内):

By the way, though I can't recall the situation now, but I had something similar with python2 only too (I haven't even tried this with 3), I thought this should work (within a class):

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 试过这个,但例如这有效:

However it causes python2 saying: NameError: name 'self' is not defined I haven't tried this with python3 yet, but for example this works:

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

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

If I change the relevant part of the previous example to this one (ok the example itself is a bit stupid, but it shows my problem at least). Now I am very curious, why self seems not to be defined for this first example but it is for the second? It seems with dicts, I have similar problem that my original question is about, but with list generator expression it works, but not in python3. Hmmm ...

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

After my python2 -> 3 problem I mentioned this, since all of these seems to be about the problem that something is not defined according to python interpreter (and maybe the second part of my question is unrelated?). I feel quite confused now. Please enlighten me about my mistake (since I am sure I missed something of course).

推荐答案

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

As Wooble says, the issue is that classes don't have a lexical scope (actually, in either Python 2 or Python 3). Instead, they have a local namespace that does not constitute a scope. This means that expressions within the class definition have access to the content of the namespace:

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

但是在类主体中引入的作用域不能访问其命名空间:

but scopes introduced within the body of the class do not have access to its namespace:

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 列表推导式引入新的作用域:

The difference between Python 2 and Python 3 is that in Python 2 list comprehensions do not introduce a new scope:

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

而在 Python 3 中则是这样:

whereas in Python 3 they do:

[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 中都有自己的作用域.

This was changed in Python 3 for a couple of reasons, including to make list comprehensions behave the same way as generator-expressions (genexps); (a for a in range(3)) has its own scope in both Python 2 and Python 3.

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

So, within the body of a class, a Python 2 genexp or a Python 3 listcomp or genexp introduces a new scope and therefore does not have access to the class-definition local namespace.

让 genexp/listcomp 访问类定义命名空间中的名称的方法是引入一个新的作用域,使用函数或 lambda:

The way to give the genexp/listcomp access to names from the class-definition namespace is to introduce a new scope, using a function or a lambda:

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


您的 eval 示例的问题在于 eval 默认在本地范围内评估其参数;因为 Python 2 列表推导式具有上述共享封闭范围的行为,所以 eval 可以访问方法范围,但是 genexp 或 Python 3 listcomp 局部范围只有编译器可以从封闭范围中分辨出来的任何内容范围(因为 genexp/listcomp 范围是一个闭包):

The issue with your eval example is that eval by default evaluates its argument in the local scope; because Python 2 list comprehensions have the above behaviour of sharing the enclosing scope the eval can access the method scope, but a genexp or Python 3 listcomp local scope only has whatever the compiler can tell is required from the enclosing scope (since a genexp/listcomp scope is a closure):

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 所说,您应该使用 getattr 而不是 eval.

As Martijn says, instead of eval you should use getattr.

这篇关于使用 python3 的列表生成器表达式中的未定义全局,与 python2 一起使用,需要进行哪些更改?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆