[Python-ideas] generic Liftable abc-mixin breaks at MRO

Stephan Sahm Stephan.Sahm at gmx.de
Thu Dec 10 03:58:37 EST 2015


Dear all,

I think I found a crucial usecase where the standard MRO does not work out.
I would appreciate your help to still solve this usecase, or might MRO even
be adapted?

The idea is to build a generic Lift-type which I call this way because the
derived classes should be able to easily lift from subclasses. So for
example if I have an instance *a* from *class A* and a *class B(A)* I want
to make *a* an instance of *B* in a straightforward way.

My implementation (Python 2.7):

import abc
import inspect

def use_as_needed(func, kwargs):
    meta = inspect.getargspec(func)
    if meta.keywords is not None:
        return meta(**kwargs)
    else:
        # not generic super-constructor - pick only the relevant subentries:
        return func(**{k:kwargs[k] for k in kwargs if k in meta.args})

class Liftable(object):
    __metaclass__ = abc.ABCMeta

    def __init__(self, **kwargs):
        use_as_needed(super(Liftable,self).__init__, kwargs)
        use_as_needed(self.__initialize__, kwargs)

    @abc.abstractmethod
    def __initialize__(self, **kwargs):
        return NotImplemented()

class NoMatchingAncestor(RuntimeError):
    pass

class NotLiftable(RuntimeError):
    pass

def lift(self, new_class, **kwargs):
    # Stop Conditions:
    if self.__class__ is new_class:
        return # nothing to do
    elif new_class is object: # Base Case
        # break recursion at once:
        raise NoMatchingAncestor()
    elif new_class.__base__ is not Liftable: #to ensure this is save
        raise NotLiftable("Class {} is not Liftable (must be first
parent)".format(new_class.__name__))

    # recursive case:
    if not self.__class__ is new_class.__bases__[1]:
        lift(self, new_class.__bases__[1], **kwargs)
    # own case:
    self.__class__ = new_class
    use_as_needed(self.__initialize__, kwargs)


and the example usecase:

class A(object):
    def __init__(self, a):
        self.a = a

class B(Liftable, A):
    def __initialize__(self, b):
        self.b = b

a = A(1)
print a.a, a.__class__
# 1 <class '__main__.A'>

lift(a, B, b=2)
print a.a, a.b, a.__class__
# 1 2 <class '__main__.B'>


this works so far, however if I now put a further level of Liftable (which
in principal already works with the generic definition

class C(Liftable, B):
    def __initialize__(self, c):
        self.c = c


I get the error

TypeError: Error when calling the metaclass bases Cannot create a
consistent method resolution order (MRO) for bases Liftable, B


​I read about MRO, and it seems to be the case that this setting somehow
raises this generic Error, however I really think having such a Lifting is
save and extremely useful​ - how can I make it work in python?

(one further comment: switching the order of inheritance, i.e. class B(A,
Liftable) will call A.__init__ before Liftable.__init__ which makes the
whole idea senseless)

Any constructive help is appreciated!
best,
Stephan
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20151210/c15571e5/attachment.html>


More information about the Python-ideas mailing list