I’m defining several classes intended to be used for multiple inheritance, e.g.:
class A:
def __init__(self, bacon = None, **kwargs):
self.bacon = bacon
if bacon is None:
self.bacon = 100
super().__init__(**kwargs)
class Bacon(A):
def __init__(self, **kwargs):
"""Optional: bacon"""
super().__init__(**kwargs)
class Eggs(A):
def __init__(self, **kwargs):
"""Optional: bacon"""
super().__init__(**kwargs)
class Spam(Eggs, Bacon):
def __init__(self, **kwargs):
"""Optional: bacon"""
super().__init__(**kwargs)
However, I have multiple classes (e.g. possibly Bacon, A, and Spam, but not Eggs) that care about when their property bacon is changed. They don’t need to modify the value, only to know what the new value is, like an event. Because of the Multiple Inheritance nature I have set up, this would mean having to notify the super class about the change (if it cares).
I know that it might be possible if I pass the class name to the method decorator, or if I use a class decorator. I don’t want to have all the direct self-class referencing, having to create lots of decorators above each class, or forcing the methods to be the same name, as none of these sound very pythonic.
I was hoping to get syntax that looks something like this:
@on_change(bacon)
def on_bacon_change(self, bacon):
# read from old/new bacon
make_eggs(how_much = bacon)
I don’t care about the previous value of bacon, so that bacon argument isn’t necessary, if this is called after bacon is set.
-
Is it possible to check if a super class has a method with this
decorator? -
If this isn’t feasible, are there alternatives to passing events like
this, up through the multiple-inheritance chain?
EDIT:
The actual calling of the function in Spam would be done in A, by using a @property and @bacon.setter, as that would be the upper-most class that initializes bacon. Once it knows what function to call on self, the problem only lies in propagating the call up the MI chain.
EDIT 2:
If I override the attribute with a @bacon.setter, Would it be possible to determine whether the super() class has a setter for bacon?
What you call for would probably be nicely fit with a more complete framework of signals, and so on – maybe even invite for Aspected Oriented Programing.
Without going deep into it however, a metaclass and a decorator can do just what you are asking for – I came up with these, I hope they work for you.
If you’d like to evolve this in to something robust and usable, write me – if nothing like this exists out there, it wouldbe worth to keep an utility package in pipy for this.
(tested in Python 3.2 and Python 2.6 – changing the declaration of the “A” class for
Python 2 metaclass syntax)
edit – some words on what is being done
Here is what happens:
The metaclass picks all methods marked with the “on_close” decorator, and register then in a dictionary on the class – this dictionary is named
_watched_attrsand it can be accessed as a normal class attribute.The other thing the metaclass does is to override the
__setattr__method for the clas once it is created. This new__setattr__just sets the attribute, and then checks the_wacthed_attrsdictionary if there are any methods on that class registered to be called when the attribute just changed has been modified – if so, it calls it.The extra indirection level around
watcher_setattr(which is the function that becomes each class’s__setattr__is there so that you can register different attributes to be watched on each class on the inheritance chain – all the classess have indepently acessible_watched_attrsdictionaries. If it was not for this, only the most specilized class on the inheritance chain_watched_attrswould be respected.