如何继承__del__函数

问题描述:

我正在阅读Python Essential Reference 4th ed。我无法弄清楚如何在下面的代码解决问题如何继承__del__函数

class Account(object): 
    num_accounts = 0 

    def __init__(self, name, balance): 
     self.name = name 
     self.balance = balance 
     Account.num_accounts += 1 

    def __del__(self): 
     Account.num_accounts -= 1 

    def deposit(self, amt): 
     self.balance += amt 

    def withdraw(self, amt): 
     self.balance -= amt 

    def inquire(self): 
     return self.balance 

class EvilAccount(Account):  
    def inquire(self): 
     if random.randint(0,4) == 1: 
      return self.balance * 1.1 
     else: 
      return self.balance 

ea = EvilAccount('Joe',400) 

如果我理解正确的话,EA对象超出范围的程序结束和继承__del__功能应该被调用,正确的,当?我在__del__中收到'NoneType' object has no attribute num_accounts。为什么在__init__函数中没有提前投诉?

the docs

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

ea超出范围时,您无法控制内存的释放方式。看来AccountNone在这个阶段的参考

为什么你认为你需要一个__del__方法呢?

+0

我正在学习python,所以我不知道我是否应该在程序中包含__del__ ...我猜这个函数对你没有用处?如果没有类变量,你会如何计算一个类的instatiated对象? – Kokas 2013-05-03 07:39:26

+0

另外,即使ea是垃圾收集器,也不存在对象......为什么我不能使用类名来访问它的一个变量?这在Python中不可能吗? – Kokas 2013-05-03 07:40:47

+0

@Kokas:这是记录,比较我的答案。 – 2013-05-03 07:41:45

当解释器退出时,Account引用在获得EvilAccount()实例之前被移除。结果,Account现在是`无。

一种解决方法是使用type(self)而不是直接引用类的名字,但是这将让每类计数,所以EvilAccount将有它自己的计数器:

def __init__(self, ...): 
    # ... 
    type(self).num_accounts += 1 

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

另一种选择当调用__del__时,检查Account是否仍然存在;简单地捕捉到了异常:

def __del__(self): 
    try: 
     Account.num_accounts -= 1 
    except AttributeError: 
     pass # Account has already been reaped 

其他人回答为什么会这样,但你应该做的,而不是,试试这个:

import weakref 
class classproperty(object): 
    def __init__(self, f): 
     self.f = f 
    def __get__(self, obj, owner): 
     return self.f(owner) 

class Account(object): 
    _active = weakref.WeakSet() 

    @classproperty 
    def num_accounts(self): 
     return len(Account._active) 

    def __init__(self, name, balance): 
     self.name = name 
     self.balance = balance 
     Account._active.add(self) 

    def deposit(self, amt): 
     self.balance += amt 

    def withdraw(self, amt): 
     self.balance -= amt 

    def inquire(self): 
     return self.balance 

>>> Account.num_accounts 
0 
>>> a = [Account('a', 0), Account('b', 0)] 
>>> Account.num_accounts 
2 
>>> a.pop() 
<__main__.Account object at 0x02F8D650> 
>>> Account.num_accounts # Interactive session is holding onto the popped account in '_' 
2 
>>> Account.num_accounts # but now it has gone. 
1 

因此,而不是计算有多少实例存在,只要保持收集所有当前实例。 WeakSet不会阻止它们被销毁,因此它只会准确地跟踪仍然存在的实例。

尽管如此,在你认为你已经失去它们后,实例很容易停留:如果任何事情抛出一个异常,那么栈帧中的所有局部变量将保持活动状态,直到引发下一个异常。在这种情况下,您可能还需要一个明确的close()方法,您可以在某人关闭该帐户并明确从活动集中删除该实例时使用该方法。