MRO problems with diamond inheritance?

  • Thread starter John Perks and Sarah Mount
  • Start date
J

John Perks and Sarah Mount

Trying to create the "lopsided diamond" inheritance below:
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: Error when calling the metaclass bases
Cannot create a consistent method resolution
order (MRO) for bases D1, D2

Is this as intended? Especially since reversing the order makes it OK:
(<class '__main__.D'>, <class '__main__.D2'>, <class '__main__.D1'>,
<class '__m
ain__.B'>, <type 'object'>)

Why should order of base classes matter? This only affects types,
old-style classes are unaffected.

The workaround to this problem (if it is a problem and not a feature
that I'm misunderstanding) is to put more-derived classes at the front
of the list. I ran into this with dynamically-created classes, and
sorting the bases list with the following comparator put them right:

def cmpMRO(x,y):
if x == y:
return 0
elif issubclass(x, y):
return -1
elif issubclass(y, x):
return +1
else:
return cmp(id(x), id(y))

Incidentally, is it safe to have an mro() method in a class, or is this
as reserved as the usual __reserved_words__? Does Python use mro() (as
opposed to __mro__) internally or anything?

Thanks

John
 
M

M.E.Farmer

I knew it was a feature but had only a few minutes to answer and was
playing it safe ;)
You would be surprised how many answers you can squeeze out of that one
URL.
That whole page is worth a month of study( for me anyway ).
I am still trying to grasp the 'purpose' of classmethods.
 
M

Michele Simionato

John & Sarah:
Incidentally, is it safe to have an mro() method in a class, > or is this
as reserved as the usual __reserved_words__? Does Python > use mro() (as
opposed to __mro__) internally or anything?

..mro() is a metamethod, it is a method of the metaclass, not of the
class. So you can override it without problems, and still access it as
type.mro(cls).

For more about metamethods see
http://www-128.ibm.com/developerworks/linux/library/l-pymeta2/index.html

Michele Simionato
 
M

Michele Simionato

M.E. Farmer:
You would be surprised how many answers you can squeeze out of that one
URL.
That whole page is worth a month of study( for me anyway ).

One month only? You must be pretty smart, one could easily extract
a book from that page ;)
I am still trying to grasp the 'purpose' of classmethods.

In my personal opinion classmethods and staticmethods could (and
possibly
should) be removed from the language; a part for that consideration,
the typical
use for classmethods is as object factories, to provide alternative
constructors.

Michele Simionato
 
M

Michele Simionato

BTW, what it your use case? I have yet to see a single compelling use
case for multiple inheritance, so I am
curious of what your design is.

Michele Simionato
 
M

M.E.Farmer

Michele said:
M.E. Farmer:


One month only? You must be pretty smart, one could easily extract
a book from that page ;)
No, I was being 'conservative'.
I am not as smart as the fellow that wrote that MRO paper ;)
Honestly, I have had that bookmarked for years...
There are some deep conceps on that page and I have not had a 'use
case' for most of them.
I would gladly welcome the book, you gonna write it!?
Something that covers ( with lots of 'real-life' examples ):
classes ( I see a lot of questions on c.l.py about basic class use )
special methods and class customization ( putting classes to work )
metaclasses ( what they are and when you might need them )
inheritance ( covering all flavors subclassing,mixin,etc.. )
descriptors ( what are they and why you should care )
types ( creating new ones, subclassing old ones, etc... )
old style / newstyle gotchas for classes / metaclasses
and maybe a few pages on decorators.

It is all well and good to have advanced OO in the language,
but it would be better if there was a 'reason' for it all.
In my personal opinion classmethods and staticmethods could (and
possibly
should) be removed from the language; a part for that consideration,
the typical
use for classmethods is as object factories, to provide alternative
constructors.

Michele Simionato

Thank you, I had a feeling that it was just extra fluff.
But I felt/feel that way about list comprehensions and decorators too
;)
M.E.Farmer
 
M

Michele Simionato

Well, writing that book would be a major undertaking that I am not
willing
to take any soon ;)

However, all you are asking for is already in my lectures at ACCU:
http://www.reportlab.org/~andy/accu2005/pyuk2005_simionato_wondersofpython.zip
From the README:

"""
<snip>
Generally speaking these lectures are unpolished, too concise, with
more code than words, and with cutting edge/experimental code.
Read them at your risk and peril. Take in account the fact that they
were prepared as a last minute replacement for Alex Martelli's
tutorial,
with a limited amount of time and a very advanced program to follow.

My main concern in preparing these notes was to give the readers a few
*ideas*, not polished solutions. If you are reading these notes, you
will be more than capable to customize these ideas to your own
situation
and to fix the unavoidable little bugs, imperfections, annoyances.

Whereas I recommend the first lecture about iterators and generators
to everybody, take in account than the second and especially the
third lecture may cause your head to explode. I do not take any
responsability in that case.
"""
Michele Simionato
 
M

M.E.Farmer

Well, writing that book would be a major undertaking that I am not
willing to take any soon ;)
Well at least you didn't say NO, so when you DO write the book I will
buy a copy.
However, all you are asking for is already in my lectures at ACCU
How many people can say ' No book just these lectures that cover
everything you want '
I knew I was asking the right person ;)
Now I just need to read your lectures...but first I am going to wrap my
head in duct tape.
( prevents/contains explosions )
Thanks for your time,
M.E.Farmer
 
A

Aahz

BTW, what it your use case? I have yet to see a single compelling use
case for multiple inheritance, so I am curious of what your design is.

Dunno whether you'd call it compelling, but my current company uses
multiple inheritance to compose a mega-class with different methods
depending on what the mega-class's capabilities are. It's a pretty
baroque design, admittedly, but I'm not sure how I'd do it differently
from scratch. We've even implemented our own super() for cooperative
method calls with classic classes.

(This application was started in Python 1.4; we're slowly refactoring
it, but we're still using Python 2.2/2.3 -- we didn't get rid of 1.5.2
until last July. Now you know why I've gotten even more conservative
since I started this job last June...)
 
J

John Perks and Sarah Mount

Michele Simionato said:
BTW, what it your use case? I have yet to see a single compelling use
case for multiple inheritance, so I am
curious of what your design is.

Before I start, I should mention that my workaround I listed previously
is bogus, but I think this:

listOfBases.sort(reverse=True, key=lambda b:len(b.__mro__))

has the effect of pulling the most derived classes to the front of the
bases list, which at any rate stops the exception being thrown.

My compelling use case for MI in Python is... better to model MI in Java
:)

Well, I'll come back to that. In the "real" world, I have found
single-implementation-multiple-interface inheritance (as presented by
Java and .NET) to be highly expressive while avoiding some of the issues
of multiple-implementation inheritance as presented by C++. (Though I
don't agree with Java's FUD that multpile-implementation iheritance is a
bad design choice simply because Java doesn't support it.) In one
application, we had a simple (tree-like, non-MI) hierarchy of abstract
interfaces which would be used by the API's clients:

class Thing { } // abstract
class SpecialThing : virtual public Thing { } // abstract

and the concrete subclasses (that were not exposed via the API) mirrored
it:
class ThingImpl : virtual public Thing { }
class SpecialThingImpl : virtual public SpecialThing, virtual public
ThingImpl { }

(That said, that same job sent me on a training course to learn not to
use inheritance *at* *all*, but rather cut'n'paste multiple copies of
the same code and maintain them in parallel. I got laid off when it
transpired that the entire project was just a figment of somebody's
imagination.)

I'm currently writing a package to embed Java in Python (another one,
just what the world needs), and I use Python's dynamic type creation to
"grow" a Python type hierarchy mirroring that of the underlying Java
object, which is where I'd been running into the MRO problems that
kicked off this whole thread. The Java classes are represented by Python
types, where the one for class Class is also the metaclass of all the
Java objects. Aside from those though, I've now had to model Java's MI
in both C++ and Python and found it to be surprisingly painless on both
occasions.

(In cas you're interested, it all works via:

[Python client code] --> pyJav(Python) --> Boost.Python -->
pyJav._core(C++) --> MyJNI++ --> JNI --> [Java code]

where all the stuff you've never heard of is mine. I'm aiming to keep
the pyJav C++ layer as thin as possible, and do everything I can in
Python.)

John.
 
M

Michele Simionato

AFAIK, the best use case for multiple inheritance is the plugin
pattern,
when you plug in methods in a class according to a given condition.

For instance:

if plugin == "PDF":
class DocumentGenerator(BaseGenerator, PDFMixin): pass
elif plugin == "PS":
class DocumentGenerator(BaseGenerator, PSMixin): pass
elif plugin == "HTML":
class DocumentGenerator(BaseGenerator, HTMLMixin): pass

etc. This is not a bad use case (actually it is good) still I would say
it
is not compelling. I could just add the right methods by hand:

if plugin == "PDF":
DocumentGenerator = type("DocumentGenerator", (BaseGenerator,),
PDFmethods)
elif plugin == "PS":
DocumentGenerator = type("DocumentGenerator", (BaseGenerator,),
PSmethods)
elif plugin == "HTML":
DocumentGenerator = type("DocumentGenerator", (BaseGenerator,),
HTMLmethods)
or use a different design based on composition + delegation.

The problem with MI is that it does not really give anything new that
you cannot do in other ways (at least in a dynamic language such as
Python); OTOH, it is extremely easily abused and makes difficult to
reason about code. Look to what happened to Zope 2!

Adding methods by hand is ugly, people would think twice before doing
that;
OTOH, using mixin is even worse, still it looks cool and people do not
realize how bad it is. Here I speak for personal experience, as you may

imagine, having both read and written MI hierarchies that could have
much better written without MI. Finally, let me say that cooperative
methods are terrible for maintenance. Nowadays, I tend to use MI just
for debugging (yes, a mixing is convenient, if not compelling, for
adding debugging functionality to a class).


Michele Simionato
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,983
Messages
2,570,187
Members
46,747
Latest member
jojoBizaroo

Latest Threads

Top