Chaining constructor and destructor calls together during object creation/destruction.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | def __init__(self):
"""Loop on all base classes, and invoke their constructors.
Protect against diamond inheritance."""
for base in self.__class__.__bases__:
# Avoid problems with diamond inheritance.
basekey = 'init_' + str(base)
if not hassattr(self, basekey):
setattr(self, basekey, 1)
else:
continue
# Call this base class' constructor if it has one.
if hasattr(base, "__init__"):
base.__init__(self)
def __del__(self):
"""Loop on all base classes, and invoke their destructors.
Protect against diamond inheritance."""
for base in self.__class__.__bases__:
# Avoid problems with diamond inheritance.
basekey = 'del_' + str(base)
if not hasattr(self, basekey):
setattr(self, basekey, 1)
else:
continue
# Call this base class' destructor if it has one.
if hasattr(base, "__del__"):
base.__del__(self)
|
Often, Python programmers hardcode calls to the parent's constructor and destructor. While some may prefer this, it arguably creates maintenance problems, since changing the base classes requires changes to these chaining calls. It is preferrable to have the base classes' constructors and destructors called generically, so that they never need be touched again. Since Python permits multiple inheritance, we must also protect against "diamond inheritance", as we don't wish to call any constructor or destructor twice.
incorrect behaviour - you must only worry about you direct ancestors. Why call constructors/destructors of grandancestors? Either it has been done by direct ancestors or this behavior is undesirable.
Only direct ancestors are being called. Immediate ancestors are the only base classes being called. We must screen against diamond inheritance in the case of this technique being used by all classes up the inheritance chain. If it is not, it will still work, with only a small amount of additional bookkeeping that is unnecessary.
Unless you are suggesting that grand-ancestors should not be called in general in OOP, which is not strictly a Python question.
Use issubclass? Would it be better to use issubclass instead of the setattr technique you are using? So you can call __init__ on all self.__class__.__bases__ except those which are superclasses of other bases?
I haven't tested this though:
def __init__(self): for base in self.__class__.__bases__: skip = False for b2 in self.__class__.__bases__ if (b2 is not base): if issubclass(b2,base): skip = True break if (not skip): try: base.__init__(self) except AttributeError: pass
Also it would be nice to perhaps create a metaclass that automatically chains certain methods transparently.