[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