Hello, suppose you have you have three classes A, B, C where C is a subclass of B and B of A. They provide a method with signature `filter(element)`. It is called by an external function that collects the elements and then to an instance of the classes A, B and C is given a chance to filter these elements, one at a time. So the `filter(element)` method returns `True` if the element has to be included in the final set and `False` if it doesn't, easy peasy. (This is a simplified example of a real use case).
What I want to have is that if an instance of class C has doesn't know what to return about an element, it calls `super()` to return the value from the same method implemented in its superclass, and so on up on the tree. To show it in code: class A: def filter(self, element): # the default implementation always returns True return True class B(A): def filter(self, element): if element == 'foo': return True elif element == 'bar': return False else: return super().filter(element) class C(B): def filter(self, element): if element == 'bar': return True else: return super().filter(element) def collect_elements(instance): "A semplified element collect function" all_elts = {'foo', 'bar', 'baz', 'zoo'} filtered_elts = set(el for el in all_elts if instance.filter(el)) return filtered_elts b = B() c = C() print(collect_elements(b)) print(collect_elements(c)) which run gives the following result: >>> {'foo', 'zoo', 'baz'} {'bar', 'foo', 'zoo', 'baz'} Now, what i ideally want is to get rid of that super() call at the end of the methods in classes B and c and to code that "fallback to what my superclass says" coded into A's implementation, where it kicks in if the method in the target instance returns None. So to write it in code, I would like some like: class A: def _filter_impl(self, element): # the default implementation always returns True return True def filter(self, element): # here a logic that if self._filter_impl(element) returns # None, it defaults to super(type(self), self)._filter_impl(element) # until A._filter_impl() is reached pass class B(A): def _filter_impl(self, element): if element == 'foo': return True elif element == 'bar': return False class C(B): def filter(self, element): if element == 'bar': return True I've tried some variants of the 'super()' trick, that sometimes seems to change behavior if it's written like that or like super(type(self), self) in no clear (to me, and I failed to find extensive doc on super()'s behavior) way, with things that stop working if mixins are involved (even if the mixins do not reimplement the methods involved here). Eventually i ended implementing a decorator: from functools import partial, wraps class delegate_super: """A method decorator that delegates part of the computation to the same method on the ancestor class.""" _name = None _owner = None def __init__(self, meth): self._meth = meth @wraps(meth) def wrapper(*args, **kwargs): return self.delegator(*args, **kwargs) self.wrapper = wrapper def __get__(self, instance, owner): return partial(self.wrapper, instance) def __set_name__(self, owner, name): self._owner = owner self._name = name def delegator(self, instance, *args, **kwargs): result = self._meth(instance, *args, **kwargs) if result is None: result = getattr(super(self._owner, instance), self._name)( *args, **kwargs) return result class A: def filter(self, element): # the default implementation always returns True return True class B(A): @delegate_super def filter(self, element): if element == 'foo': return True elif element == 'bar': return False class C(B): @delegate_super def filter(self, element): if element == 'bar': return True else: return super().filter(element) def collect_elements(instance): "A semplified element collect function" all_elts = {'foo', 'bar', 'baz', 'zoo'} filtered_elts = set(el for el in all_elts if instance.filter(el)) return filtered_elts b = B() c = C() print(collect_elements(b)) print(collect_elements(c)) which has the same result as before: >>> {'foo', 'zoo', 'baz'} {'bar', 'foo', 'zoo', 'baz'} I would really like to find a way to do this that doesn't involve decorating the methods in A subclasses to free the final developer to remember to import the decorator and apply it, just like I don't want him (probably me six months from now) to have to remember to add an `else: super()...` to its computation... Has anyone done this before and has any suggestion about a better way to do it? Am I getting it wrong? cheers, Alberto -- https://mail.python.org/mailman/listinfo/python-list