Python - 观察者模式 - 对象没有属性

时间:2014-12-22 11:58:26

标签: python design-patterns python-3.x finalizer

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

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'

1 个答案:

答案 0 :(得分:4)

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

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

object.__del__ documentation中有一个很大的警告:

  

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

解决方法是让您的__del__方法在面对此类异常时更加强大:

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