如何继承__del__函数

时间:2013-05-03 07:17:50

标签: python inheritance del

我正在阅读Python Essential Reference 4th ed。我无法弄清楚如何解决以下代码中的问题

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)

如果我理解正确,当程序结束并且应该调用继承的__del__函数时,ea对象超出范围,对吗?我在'NoneType' object has no attribute num_accounts收到了__del__。为什么之前没有在__init__函数中抱怨?

4 个答案:

答案 0 :(得分:3)

来自the docs

  

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

答案 1 :(得分:1)

ea超出范围时,您无法控制内存的释放方式。 Account似乎是在此阶段对None的引用

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

答案 2 :(得分:1)

解释器退出时,Account引用将被移除EvilAccount()实例。结果,Account现在是“无”。

一种解决方法是使用type(self)而不是直接引用类名,但这会使每个类的计数 ,因此EvilAccount会拥有它自己的计数器:

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

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

另一种方法是在调用Account时检查__del__是否仍然存在;只是抓住例外:

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

答案 3 :(得分:1)

其他人已经回答了为什么会这样,但是关于你应该做什么,试试这个:

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()方法,当有人关闭帐户并从活动集中明确删除实例时,您可以使用该方法。