2014-12-22 34 views
2

我尝试运行涉及观察者模式的书籍“Python Essential Reference”中的一个示例,但属性存在问题。当AccountObserver执行__del__错误引发 - 对象没有属性'观察者'。我不知道代码有什么问题,所以任何帮助都会很感激。Python - 观察者模式 - 对象没有任何属性

class Account(object): 
    def __init__(self, name, balance): 
     self.name = name 
     self.balance = balance 
     self.observers = set() 
    def __del__(self): 
     for ob in self.observers: 
      ob.close() 
     del self.observers 
    def register(self, observer): 
     self.observers.add(observer) 
    def unregister(self, observer): 
     self.observers.remove(observer) 
    def notify(self): 
     for ob in self.observers: 
      ob.update()  
    def withdraw(self, amt): 
     self.balance -= amt 
     self.notify() 


class AccountObserver(object): 
    def __init__(self, theaccount): 
     self.theaccount = theaccount 
     self.theaccount.register(self) 

    def __del__(self): 
     self.theaccount.unregister(self) 
     del self.theaccount 

    def update(self): 
     print("Balance is %0.2f" % self.theaccount.balance) 

    def close(self): 
     print("Account no longer in use") 


a = Account("Ketty", 200000) 
a_mama = AccountObserver(a) 
a_tata = AccountObserver(a) 
a.unregister(a_mama) 
a.withdraw(10) 

和输出:

Balance is 199990.00 
Account no longer in use 
Exception ignored in: <bound method AccountObserver.__del__ of <__main__.AccountObserver object at 0x024BF9F0>> 
Traceback (most recent call last): 
    File "F:\Projects\TestP\src\main.py", line 28, in __del__ 
    File "F:\Projects\TestP\src\main.py", line 13, in unregister 
AttributeError: 'Account' object has no attribute 'observers' 
Exception ignored in: <bound method AccountObserver.__del__ of <__main__.AccountObserver object at 0x024BFEB0>> 
Traceback (most recent call last): 
    File "F:\Projects\TestP\src\main.py", line 28, in __del__ 
    File "F:\Projects\TestP\src\main.py", line 13, in unregister 
AttributeError: 'Account' object has no attribute 'observers' 
+0

python 3'? –

+0

py 2.7 ..我不能重现它 – DevC

+0

@MartijnPieters我可以使用python3.4,'AttributeError:'Account'对象没有属性'observers'' –

回答

4

的Python淘模块解释器退出时。此时所有实例和类都被删除,这意味着Account.__del__可以在AccountObserver.__del__之前运行。清除类的顺序取决于全局名称空间字典顺序,这是随机使用的random hash seedAccount.__del__删除self.observers,因此任何后来致电account.unregister()的电话都将引发AttributeError

你的代码依赖于模块退出时仍然存在的类和属性。这意味着您可以同时获得KeyError错误(因为a_mama已被注销)或AttributeError,因为self.observers属性已被清除(因为Account.__del__已将其清除)。

有一个大胖子警告在object.__del__ documentation

Warning: Due to the precarious circumstances under which __del__() methods are invoked, exceptions that occur during their execution are ignored, and a warning is printed to sys.stderr instead. Also, when __del__() is invoked in response to a module being deleted (e.g., when execution of the program is done), other globals referenced by the __del__() method may already have been deleted or in the process of being torn down (e.g. the import machinery shutting down). For this reason, __del__() methods should do the absolute minimum needed to maintain external invariants. Starting with version 1.5, Python guarantees that globals whose name begins with a single underscore are deleted from their module before other globals are deleted; if no other references to such globals exist, this may help in assuring that imported modules are still available at the time when the __del__() method is called.

的解决办法是让你的__del__方法面对这样异常的更强大的:你使用`

def unregister(self, observer): 
    try: 
     self.observers.remove(observer) 
    except (KeyError, AttributeError): 
     # no such observer, or the observers set has already been cleared 
+0

python 2和python 3有什么区别? –

+0

@PadraicCunningham:默认情况下,Python 3使用随机哈希种子,因此字典顺序更不可预测。它也可以在Python 2中发生,但字典顺序更加稳定,所以除非使用'-R'开关运行Python,否则在这里不会看到它发生。 –

+0

啊我明白了,谢谢。 –