【问题标题】:How to inherit __del__ function如何继承__del__函数
【发布时间】:2013-05-03 07:17:49
【问题描述】:

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

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__ 函数中更早地抱怨?

【问题讨论】:

    标签: python inheritance del


    【解决方案1】:

    来自the docs

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

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

      • 我正在学习python,所以我不知道我是否应该在我的程序中包含del...我猜这个函数对你没有用?如果没有类变量,您还怎么计算类的实例化对象?
      • 另外,即使 ea 已被垃圾回收并且不存在任何对象...为什么我不能使用类名来访问其变量之一?这在python中不可能吗?
      • @Kokas :这是记录在案的,参见我的回答。
      【解决方案3】:

      当解释器退出时,Account 引用在EvilAccount() 实例被收割之前被删除。因此,Account 现在是“无”。

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

      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
      

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-11-24
          • 2010-09-18
          • 2010-09-18
          • 1970-01-01
          • 1970-01-01
          • 2022-11-09
          • 1970-01-01
          • 2013-07-24
          相关资源
          最近更新 更多