【问题标题】:Variable Scope In Generators In Classes类中生成器中的变量范围
【发布时间】:2017-04-12 10:29:21
【问题描述】:

我认为我很了解变量和生成器在 Python 中的工作原理。
但是,下面的代码让我很困惑。

from __future__ import print_function

class A(object):
    x = 4
    gen = (x for _ in range(3))

a = A()
print(list(a.gen))

运行代码时(Python 2),它说:

Traceback (most recent call last):
  File "Untitled 8.py", line 10, in <module>
    print(list(a.gen))
  File "Untitled 8.py", line 6, in <genexpr>
    gen = (x for _ in range(3))
NameError: global name 'x' is not defined

在 Python 3 中,它表示 NameError: name 'x' is not defined
但是,当我跑步时:

from __future__ import print_function

class A(object):
    x = 4
    lst = [x for _ in range(3)]

a = A()
print(a.lst)

该代码在 Python 3 中不起作用,但在 Python 2 或类似的函数中起作用

from __future__ import print_function

def func():
    x = 4
    gen = (x for _ in range(3))
    return gen

print(list(func()))

此代码在 Python 2 和 Python 3 或模块级别上运行良好

from __future__ import print_function

x = 4
gen = (x for _ in range(3))

print(list(gen))

该代码在 Python 2 和 Python 3 中也能正常运行。

为什么class会出错?

【问题讨论】:

    标签: python variables generator


    【解决方案1】:

    因为x 是一个类属性(静态变量),您可以像这样访问它,

    示例

    >>> class A(object):
    ...     x = 4
    ...     gen = (A.x for _ in range(3))
    ...
    >>> a = A()
    >>> list(a.gen)
    [4, 4, 4]
    

    这里甚至gen 是另一个类属性,这意味着,

    >>> b = A()
    >>> list(b.gen)
    []
    

    这给了空,因为生成器已经耗尽。


    发生这种情况是因为生成器仅在您发出 a.gen 时才被评估,此时它将无法解析名称 x

    【讨论】:

    • 我以为x类变量而不是静态变量?我弄错了吗?
    • 这不完全是True。将gen 替换为y = x+1。然后像A.y 一样访问它。它会起作用的。为什么它不适用于 gen
    • @MoinuddinQuadri 与y = x + 1x 的值被查找立即,如果在处理@987654336 的主体时设置了y 的值@ 陈述。 (x for _ in range(3)) 创建一个生成器表达式,在您实际尝试使用生成的生成器中的值之前,不会评估其主体。也就是说,x 的查找发生在 class 语句之外。
    • @MoinuddinQuadri 好点,但生成器将在稍后的时间点进行评估。
    • @nu11p01n73R 为什么lst = [x for _ in range(3)] 在 Python 3 中引发错误
    【解决方案2】:

    那是因为x 是一个类变量。在 python 中,必须使用self(例如从实例方法访问)或类名来访问类变量。

    class A(object):
        x = 4
        gen = (A.x for _ in range(3))
    
        def call_me(self):
            print self.x
    
    a = A()
    a.call_me()
    print list(a.gen)
    

    更详细的讨论见 Static class variables in PythonWhy is Python class not recognizing static variable

    【讨论】:

      【解决方案3】:

      正如其他答案所述,它是True 发生的,因为它是静态变量。但限制你的代码工作的不仅仅是那个属性。实际原因是变量的作用域和它执行的作用域。例如,创建一个类:

      class A(object):
          x = 999999
          y = x +1
      

      如果您访问它的类属性A.xA.y,它将起作用。因为,在初始化y 时,x 被替换为表达式x+1 中的值。因为x 的范围在类内。

      但是,这不会发生在生成器的情况下。即在您的示例中:

      class A(object):
          x = 4
          gen = (x for _ in range(3))
      

      当您执行list(a.gen) 时,它将在类外部执行(因为生成器在运行时进行评估)并检查当前范围内x 的引用。由于x 没有在该范围内初始化,因此会引发错误。

      当您显式初始化 x=4 时,它会起作用,因为现在生成器表达式的值是 x,它可以使用它。

      为了使您的生成器表达式正常工作,正如其他人所说,您必须将其定义为:

      class A(object):
          x = 4
          gen = (A.x for _ in range(3))
          #      ^ mentioning `A.x` is the value to access
      

      【讨论】:

      • 声明it is executed outside the class (as generators are evaluated during run-time) and checks for the reference of x **in the current scope** 可以争论。看到这个ideone.com/bgef81 结果是[6,6,6],而不是[5,5,5],为什么?
      猜你喜欢
      • 2014-07-19
      • 1970-01-01
      • 2021-02-08
      • 1970-01-01
      • 2013-11-18
      • 2017-03-26
      • 2022-01-11
      • 2011-05-21
      相关资源
      最近更新 更多