You can resort to metaprogramming methods, such as writing a metaclass that automatically fills the rest of the methods, or use introspection to look at classes in type(self).mro() to find out which methods have been overridden. However, these options firmly fall into the category of “too much magic” for me, so I would go with something simpler.
Just divide each method into two: one general and one actual. Derived classes override the actual implementation:
class FooBarBaz(object): def foo_impl(self, x): raise NotImplementedError def foo(self, x): try: return self.foo_impl(x) except NotImplementedError: try: return foo_from_bar(self.bar_impl(x)) except NotImplementedError: return foo_from_baz(self.baz_impl(x))
General logic can also be taken into account in the decorator:
def first_implemented(func): @functools.wraps def wrapper(*args, **kwargs): for f in func(*args, **kwargs): try: return f() except NotImplementedError: pass raise NotImplementedError return wrapper class FooBarBaz(object): def foo_impl(self, x): raise NotImplementedError @first_implemented def foo(self, x): yield lambda: self.foo_impl(x) yield lambda: foo_from_bar(self.bar_impl(x)) yield lambda: foo_from_baz(self.baz_impl(x))
Sven marnach
source share