2014-12-22 5 views
2

Я пытаюсь запустить один пример из книги «Основная ссылка Python» с шаблоном наблюдателя, но есть проблема с атрибутом. Когда 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' не имеет атрибута 'наблюдатели' –

ответ

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:

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, но порядок словаря более стабилен, поэтому вы не видите, чтобы это произошло здесь, если вы не запустили Python с помощью переключателя '-R'. –

+0

ах я вижу, спасибо. –

Смежные вопросы