2013-05-03 88 views
1

我正在閱讀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__函數中沒有提前投訴?

回答

3

the docs

警告:由於不穩定的情況下對其調用__del__()方法,在執行過程中發生的異常被忽略,併發出警告打印到sys.stderr代替。另外,當調用__del__()以響應模塊被刪除(例如,當程序執行完成時)時,__del__()方法引用的其他全局可能已經被刪除或正在被拆除的過程中(例如,進口機械關閉)。出於這個原因,__del__()方法應該保持外部不變量所需的絕對最小值。從版本1.5開始,Python保證在刪除其他全局變量之前,將名稱以單個下劃線開頭的全局變量從模塊中刪除;如果不存在對這種全局變量的其他引用,這可能有助於確保在調用__del__()方法時導入的模塊仍然可用。

1

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

1

當解釋器退出時,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 
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()方法,您可以在某人關閉該帳戶並明確從活動集中刪除該實例時使用該方法。