【问题标题】:Python 3 : using "all" on class variablesPython 3:在类变量上使用“all”
【发布时间】:2014-03-28 04:42:24
【问题描述】:

下面是一些引发 NameError 的代码:

class Foo:
    a = [1,2,3,4,5]
    b = all(i for i in a) # works fine !
    c = [2,3,4]
    d = all(i in a for i in c) # raises NameError : name 'a' is not defined

我对此感到很困惑--;

谢谢!

【问题讨论】:

  • 您可以使用d = (lambda a=a, c=c:all(i in a for i in c))() 作为解决方法

标签: python class static-members


【解决方案1】:

this previous question,相似但不相同。

如该答案所述:

类没有词法范围(实际上,在 Python 2 或 Python 3 中)。相反,它们有一个不构成范围的本地命名空间。这意味着类定义中的表达式可以访问命名空间的内容 [...],但在类主体中引入的范围不能访问其命名空间

在生成器表达式中,for 子句位于封闭范围/命名空间中,但目标表达式位于生成器表达式创建的新范围中。这个新作用域无权访问封闭类作用域。

简而言之,您的示例工作和失败的原因与此相同:

class Foo(object):
    a = 2
    def method(self, x=a):
        print x

但这失败了:

class Foo(object):
    a = 2
    def method(self, x):
        print a

生成器表达式的for 子句在范围上类似于方法的参数规范:它在封闭的命名空间中执行。但是 genexp 的目标表达式类似于方法体:它在自己的范围内执行,它没有访问类命名空间的权限。

(在 Python 2 中,使用列表推导而不是生成器推导将起作用,因为列表推导不会在 Python 2 中创建新范围。这被认为是一个缺陷,在 Python 3 中已更改,因此在 Python 3 中两者都不会工作。)

【讨论】:

  • @asad 在这个例子中是的,但不是在 OP 的代码中
  • @Asad:在这个例子中,是的,但这涉及到不属于原始问题的实例属性。使用 self.a 在问题中的类级别 genexp 中不起作用。
  • @BrenBarn 好的,如果有人不介意回答,最后一个问题:你为类定义的属性是否被添加到封闭范围(即你定义类的范围) 作为变量?因为否则我不明白为什么列表理解会起作用,尽管没有创建额外的范围。谢谢。
  • @Asad:它们不会被添加到封闭范围(即全局范围)中,但它们会被添加到类命名空间中。这是我从另一个问题的答案中引用的位中提到的“不构成范围的命名空间”。
【解决方案2】:

这个问题的真正答案是 BrenBarn 写的。

在 Python 2.7 中,您编写的内容不起作用,但如果您将其更改为:

class Foo:
    a = [1,2,3,4,5]
    b = all(i for i in a) # works fine !
    c = [2,3,4]
    d = all([i in a for i in c]) #works fine too!

它将按预期工作。正如 BrenBarn 指出的那样,列表推导式不会在 Python 2 中创建新范围的原因。

【讨论】:

  • 这很奇怪。我认为这仍然会引发 NameError。编辑:我站纠正。介意解释一下这是如何工作的吗?
  • 确实很奇怪。缺乏 [] 让我感到怀疑,我被吸引到第一个尝试。我不明白发生了什么事。将通过其他答案。
  • 看我的回答。这将在 Python 2 中有效,但在 Python 3 中无效。它之所以有效,是因为在 Python 2 中,列表推导与生成器推导不同,不会创建自己的范围。
猜你喜欢
  • 2020-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-11
  • 2018-04-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多