【问题标题】:Python attributeError on __del____del__ 上的 Python 属性错误
【发布时间】:2013-08-06 04:38:51
【问题描述】:

我有一个 python 类对象,我想分配一个类变量的值

class Groupclass(Workerclass):
    """worker class"""
    count = 0

    def __init__(self):
        """initialize time"""
        Groupclass.count += 1
        self.membercount = 0;
        self.members = []

    def __del__(self):
        """delte a worker data"""
        Groupclass.count -= 1


if __name__ == "__main__":
    group1 = Groupclass()

这个执行结果是正确的,但是有一个错误信息说:

Exception AttributeError: "'NoneType' object has no attribute 'count'" in <bound method Groupclass.__del__ of <__main__.Groupclass instance at 0x00BA6710>> ignored

谁能告诉我我做错了什么?

【问题讨论】:

  • 您的Workerclass 基类似乎有一个__del__ 方法试图对count 执行某些操作,但您没有在此处分享该方法。
  • 对不起,我不知道这是否相关...
  • 您的消息只是警告,不是错误。 __del__ 方法中的 Something 尝试使用 count 属性做某事但失败了。
  • 除了Groupclass.count -= 1 之外,__del__ 内部实际上什么都没有。困扰我的也是如此,因为我还没有做任何事情。
  • 但这是生成该错误消息的代码;这是相当相关的。

标签: python class attributeerror del


【解决方案1】:

您的 __del__ 方法假定该类在被调用时仍然存在。

这个假设是不正确的。当您的 Python 程序退出时,Groupclass 已被清除,现在设置为 None

先测试类的全局引用是否还存在:

def __del__(self):
    if Groupclass:
        Groupclass.count -= 1

或使用type() 获取本地参考:

def __del__(self):
    type(self).count -= 1

但请注意,这意味着如果 Groupclass 被子类化,count 的语义会发生变化(每个子类都有一个 .count 属性,而只有 Groupclass 具有一个 .count 属性)。

引用__del__钩子文档:

警告:由于调用__del__() 方法的不稳定环境,在其执行过程中发生的异常将被忽略,而是向sys.stderr 打印警告。此外,当__del__() 被调用以响应模块被删除时(例如,当程序执行完成时),__del__() 方法引用的其他全局变量可能已经被删除或正在被拆除(例如进口机器关闭)。出于这个原因,__del__() 方法应该做维护外部不变量所需的绝对最小值。从 1.5 版开始,Python 保证名称以下划线开头的全局变量会在其他全局变量被删除之前从其模块中删除;如果不存在对此类全局变量的其他引用,这可能有助于确保在调用 __del__() 方法时导入的模块仍然可用。

如果您使用的是 Python 3,则需要额外注意两点:

  • CPython 3.3 自动将randomized hash salt 应用于globals 字典中使用的str 键;这也会影响清理全局变量的顺序,可能您只在 部分运行中看到问题。

  • CPython 3.4 不再按照Safe Object Finalization 将全局变量设置为None(在大多数情况下);见PEP 442

【讨论】:

  • 谢谢它现在正在工作。现在我的问题是,如果我想 del 一个 Groupclass 对象,Groupclass 对象或“Groupclass”本身必须在 del 被执行之前存在,所以看起来该语句在某种程度上是多余的?
  • @Chandler:不,当解释器存在时,可以在实例之前清理全局名称; Groupclass 名称已被清理。您也可以改用type(self).count,除非您的子类没有自己的计数。
  • 仍然不太了解这个概念,但谢谢,我会调查一下!
  • "从 1.5 版开始,Python 保证在删除其他全局变量之前从其模块中删除名称以单个下划线开头的全局变量;如果不存在对此类全局变量的其他引用,这可能有助于确保在调用 __del__() 方法时,导入的模块仍然可用。”这是否意味着如果我重命名为_Groupclass,问题就解决了?
  • @acai:没有。这意味着绑定到以_开头的名称的对象可以依靠不以_开头的名称稍后被删除,因此您仍然可以依赖导入的模块(通常具有非私有名称)仍然可用。
【解决方案2】:

__del__()方法被调用时,Groupclass可能会被垃圾回收机制回收,所以使用Groupclass.xxx可能会失败。但是您可以通过self.__class__.count 访问count 变量。代码如下:

def __del__(self):
    self.__class__.count -= 1

【讨论】: