【问题标题】:Python Why am I getting not defined error?Python 为什么我得到未定义的错误?
【发布时间】:2018-02-21 09:56:40
【问题描述】:

当我在类代码块中使用任何类变量时,我会收到未定义的错误。我该如何解决?

例子

class A():
    __hidden_number__ = 5
    __no_hidden_number__ = A.__hidden_number__ + 4 # to cause error. why ?

谢谢。

【问题讨论】:

  • 试试__no_hidden_number__ = __hidden_number__ + 4
  • 因为A尚未定义。
  • @juanpa.arrivillaga 不。您在A 范围内。你指的是A.A。检查我的答案并测试 sn-p。
  • @Jean-FrançoisFabre 这听起来有误导性。 A 有一个外部作用域。如果封闭的命名空间中已经有一个AA.__hidden_number__ 将引用它。在类声明中,您当前声明的类的 name 不是解析层次结构的一部分。
  • @dhke:你是说我没有使用正确的词汇?可能的。你能帮忙吗?

标签: python


【解决方案1】:

由于这可能有点不清楚(现有答案很好),让我补充一下:

class A:
    pass

is equivalent to

A = type('A', (object,), {})

我们在类声明中编写的内容本质上是构建那个(这里是空的)字典,并且发生在变量A 被分配之前。由于在类声明运行时,我们在命名空间层次结构中——还没有——名称为 A 的变量,所以 A 不可用。

该类存在,它也有类型名称 'A',但尚未分配给变量A

class A: pass-version 和显式type 调用之间的区别在于语法糖:使用class 时,嵌套在类中的所有内容都会自动命名为类字典。

这就是它起作用的原因:

class A:
   var = 1
   var2 = var + 1

但正如我们在上面看到的,最终将保存我们的新类的局部变量 A 仅在类声明已经被评估后才被分配,因此在其中不可用。

如果我们反汇编一个类 decl,我们会得到:

  import codeop, dis
  dis.disassemble(codeop.compile_command('class A:\r\n    pass'))

1           0 LOAD_CONST               0 ('A')
            3 LOAD_CONST               3 (())
            6 LOAD_CONST               1 (<code object A at 0x80076ff30, file "<input>", line 1>)
            9 MAKE_FUNCTION            0
           12 CALL_FUNCTION            0
           15 BUILD_CLASS         
           16 STORE_NAME               0 (A)
           19 LOAD_CONST               2 (None)
           22 RETURN_VALUE  

1922 是自动添加的,因为代码对象必须返回一个值。在我们的示例中,我们可以忽略这些。

这里还发生了什么?

  • 0:将字符串 'A' 推入堆栈。这将成为类的名称(type() 的第一个参数)。
  • 3:推入一个空元组,父类列表
  • 6:加载类声明的代码对象。
  • 912 运行类声明代码对象。这就是例如我们的(类)变量赋值。它返回类字典。
  • 15 将类构建到堆栈上。这相当于type('A', (), class_dict),其中的参数正是此时堆栈上的值。

最后:

  • 16 将新创建的类存储到名为 A 的本地命名空间中。

如我们所见,在将类分配给其变量 A (16) 之前,类声明已运行 (12)。

注意,在 Python 3 中反汇编是不同的,因为 python 作为一个特殊的内置函数class builder function

是的,这个答案太长了 ;-)。

【讨论】:

  • 现在我明白了。这只是 不能A 相同,因为它正在构建中。所以我们都是对的。您的回答是我忽略的一个不错的附加组件。
  • @Jean-FrançoisFabre 我并不是想说你错了,但细节有点糟糕。 Python 是一个堆栈机器,区分“堆栈上的类型对象 'A'”和“名称为 A 以保存该类型对象的变量”在这里有所不同。
【解决方案2】:

您的类变量已经在 A 命名空间中。

所以你只需要这样做:

__no_hidden_number__ = __hidden_number__ + 4

否则您正在尝试访问 A.A,但它还不存在。

为了说明我所说的,“有效”(但不是你想要的):

class A():
    class A():
        __hidden_number__ = 4
    __no_hidden_number__ = A.__hidden_number__ + 4
A()  # that works

没有:

class A():
    class B():
        __hidden_number__ = 4
    __no_hidden_number__ = A.__hidden_number__ + 4  # B.__... would've worked
A()  # beeep: NameError: name 'A' is not defined

请注意,您应该避免使用尾部双下划线 (__xxx__) 定义私有属性。它可以工作,但这通常是为 python 特殊类成员(__class____file____eq__ 等)保留的。名称修改(使对象“私有”)已经使用 2 个前导下划线发生。

【讨论】:

  • 请注意,问题是为什么会出现错误。好吧,A 在类声明完成之前没有定义。
  • @Jean-FrançoisFabre 呃?是的?名称A 严格在类声明之后分配。
  • 试试我的sn-p,然后把内部的A改成B看看会发生什么
  • @Jean-FrançoisFabre 很抱歉。并不是要停止您的编辑。
  • @ChristianDean 没有汗水。没有回滚,只是添加了被删除的行。感谢您的编辑(但:))。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-03
  • 2017-03-29
  • 2014-09-12
  • 2019-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多