On Thu, Oct 12, 2017 at 5:07 PM, Alberto Berti <albe...@metapensiero.it> wrote: > 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: > > > [SNIP] > > > 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
My initial reaction is: is this really worth it? This seems like an awful lot of code and added complexity in order to do away with two lines. It's a lot easier to reason about "return super().filter(element)" and verify that it does the right thing than for the complicated descriptor above. > 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? If you don't want an explicit super() call and you don't want the decorator to be explicit either then most likely what you need is a metaclass that will automatically wrap specific methods with your decorator. Metaclasses are inherited, so all you have to do is set it for A and it will automatically apply itselt to B and C as well. -- https://mail.python.org/mailman/listinfo/python-list