为什么我在 Python class 定义的生成器中得到这个 NameError?

Why do I get this NameError in a generator within a Python class definition?

在 Python 3.5.0 中此代码:

a = (1,2)
class Foo(object):
    b = (3,4)
    c = tuple((i,j) for j in b for i in a)
    d = tuple((i,j) for i in a for j in b)

产生:

Traceback (most recent call last):
  File "genexprtest.py", line 2, in <module>
    class Foo(object):
  File "genexprtest.py", line 5, in Foo
    d = tuple((i,j) for i in a for j in b)
  File "genexprtest.py", line 5, in <genexpr>
    d = tuple((i,j) for i in a for j in b)
NameError: name 'b' is not defined

为什么会出现此错误?为什么我在上一行没有得到这个错误?

我花了很长时间进行试验,并且我对为什么会出现此错误有一个理论。我不确定,但这确实解释了为什么它适用于 c 而不是 d。我希望这对你有帮助,如果你不同意,请发表评论:)

def Tuple(this):
    print(a) # this always works
    try:
        print(b) # this always gives an error
    except NameError:
        print("...b is not defined")
    try:
        return tuple(this) # this only gives an error for d and e
    except NameError:
        print("...couldn't make it a tuple")


a = (1,2)     
class Foo(object):
    b = (3,4)
    c = Tuple((i,j) for j in b for i in a)
    d = Tuple((i,j) for i in a for j in b)
    e = Tuple((i,j,k) for i in a for j in b for k in (5, 6))
    f = Tuple((i,j,k) for j in b for i in (5, 6) for k in a)

    print("\nc:", c,"\nd:", d,"\ne:", e,"\nf:", f)

发生了什么:每次我调用Tuple()函数时,b没有定义,但是a总是被定义。这解释了为什么 de 出现错误,但它没有解释为什么 cf 即使 b 是 'not defined' 也能工作]

我的理论: 第一个 for 循环是在将整个事物转换为元组之前计算的。例如,如果您尝试这样做:Tuple((a, b, c) for a in loop1, for b in loop2 for c in loop3),在 Foo class 中它会首先计算 for a in loop1,然后它会移动到 foo 并计算循环 2 和 3。

总结:

  1. 先做for循环
  2. 移动到元组函数
  3. 做剩下的循环
  4. 如果第二个或第三个循环中的变量在 class Foo
  5. 中,则会发生错误

在我看来,错误的产生是因为b被定义为class变量。要正确使用它,您需要这样对待它 (self.b)。 另外,你应该使用构造函数:

a = (1, 2)

class Foo(object):
    def __init__(self):
        self.b = (3, 4)
        self.c = tuple((i, j) for j in self.b for i in a)
        self.d = tuple((i, j) for i in a for j in self.b)

这是一个更清晰的代码。它表现得很好。希望对你有帮助。

编辑:如果您不想使用 __init__,也可以使用以下方法获得 cd

a = (1, 2)

class Foo(object):
    b = (3, 4)

    def get_c(self):
        return tuple((i, j) for j in self.b for i in a)

    def get_d(self):
        return tuple((i, j) for i in a for j in self.b)

这也很好用。 您可以像这样尝试这两种实现方式:

inst = Foo()
# 1st one
print(inst.c)
print(inst.d)
# 2nd one
print(inst.get_c())
print(inst.get_d())

这是因为表达式for i in a有一个局部变量范围,而表达式for j in b在范围内,因此,没有b 被发现。
实际上,如果你写 c = tuple((i, j) for i in a for j in b),它会抛出同样的异常。

解决方案将 b 放入 class 定义的范围(正如您已经做的那样)并通过 self.b 引用它。

针对您的具体情况的 解决方案 是使用 itertools:

d = tuple(itertools.product(a, b))

看似意外行为的解释是双重的:

  1. 裸 class 属性,例如 b 只能在 root class 范围 中访问。见 pep 227:

    Names in class scope are not accessible. Names are resolved in the innermost enclosing function scope. If a class definition occurs in a chain of nested scopes, the resolution process skips class definitions.

  2. 生成器中的嵌套循环并不像您预期​​的那样运行。第一个循环实际上是最外层的,第二个是最内层的。来自 python docs:

    Subsequent for clauses cannot be evaluated immediately since they may depend on the previous for loop. For example: (x*y for x in range(10) for y in bar(x)).

第二点可以用换行来说明。

d = tuple((i,j) 
    for i in a
        for j in b)

这意味着 b 实际上是从内部循环(嵌套范围)引用的,因此抛出 NameError。然而,在第一个生成器中,引用位于外部,它工作正常。