То, к чему вы обращаетесь, вероятно, будет хорошо соответствовать более полной структуре сигналов и т. Д. - возможно, даже пригласит для Аспектно-ориентированного программирования.
Однако, не углубляясь в это, метакласс и декоратор могут сделать то, что вы просите - я придумал это, я надеюсь, что они сработают для вас.
Если выхотел бы развить это во что-то надежное и пригодное для использования, напишите мне - если ничего подобного не существует, стоило бы сохранить пакет утилит в pipy для этого.
def setattr_wrapper(cls):
def watcher_setattr(self, attr, val):
super(cls, self).__setattr__(attr, val)
watched = cls.__dict__["_watched_attrs"]
if attr in watched:
for method in watched[attr]:
getattr(self, method)(attr, val)
return watcher_setattr
class AttrNotifier(type):
def __new__(metacls, name, bases, dct):
dct["_watched_attrs"] = {}
for key, value in dct.items():
if hasattr(value, "_watched_attrs"):
for attr in getattr(value, "_watched_attrs"):
if not attr in dct["_watched_attrs"]:
dct["_watched_attrs"][attr] = set()
dct["_watched_attrs"][attr].add(key)
cls = type.__new__(metacls, name, bases, dct)
cls.__setattr__ = setattr_wrapper(cls)
return cls
def on_change(*args):
def decorator(meth):
our_args = args
#ensure that this decorator is stackable
if hasattr(meth, "_watched_attrs"):
our_args = getattr(meth, "_watched_attrs") + our_args
setattr(meth, "_watched_attrs", our_args)
return meth
return decorator
# from here on, example of use:
class A(metaclass=AttrNotifier):
@on_change("bacon")
def bacon_changed(self, attr, val):
print ("%s changed in %s to %s" % (attr, self.__class__.__name__, val))
class Spam(A):
@on_change("bacon", "pepper")
def changed(self, attr, val):
print ("%s changed in %s to %s" % (attr, self.__class__.__name__, val))
a = A()
a.bacon = 5
b = Spam()
b.pepper = 10
b.bacon = 20
(протестировано в Python 3.2 иPython 2.6 - изменение объявления класса «A» для синтаксиса метакласса Python 2)
edit - несколько слов о том, что делается Вот что происходит: метакласс выбирает все отмеченные методыс помощью декоратора «on_close» и зарегистрируйтесь в словаре класса - этот словарь называется _watched_attrs
, и к нему можно обращаться как к обычному атрибуту класса.
Другая вещь, которую метакласс делает, - переопределениеметод __setattr__
для класа после его создания.Этот новый __setattr__
просто устанавливает атрибут, а затем проверяет словарь _wacthed_attrs
, есть ли в этом классе какие-либо методы, зарегистрированные для вызова при изменении только что измененного атрибута - если так, он вызывает его.
Дополнительный уровень косвенности около watcher_setattr
(это функция, которая становится __setattr__
каждого класса, так что вы можете зарегистрировать различные атрибуты, которые будут наблюдаться в каждом классе в цепочке наследования - все классы имеют доступ независимо _watched_attrs
словари. Если бы не это, был бы соблюден только самый специализированный класс в цепочке наследования _watched_attrs
.