diff options
author | michele.simionato <devnull@localhost> | 2008-10-15 05:34:50 +0000 |
---|---|---|
committer | michele.simionato <devnull@localhost> | 2008-10-15 05:34:50 +0000 |
commit | 80561be657f8b7e7717112e1b3a6cc5e179d738b (patch) | |
tree | 872bcd9b013c364bdac2386b30d098b8f65b6aab /pypers | |
parent | 01eef208eb48cdb271c57a6f3218b75112d8d669 (diff) | |
download | micheles-80561be657f8b7e7717112e1b3a6cc5e179d738b.tar.gz |
Various stuff
Diffstat (limited to 'pypers')
-rwxr-xr-x | pypers/super/ex.py | 35 | ||||
-rw-r--r-- | pypers/super/super2.txt | 779 |
2 files changed, 22 insertions, 792 deletions
diff --git a/pypers/super/ex.py b/pypers/super/ex.py index 6a8489a..089dea9 100755 --- a/pypers/super/ex.py +++ b/pypers/super/ex.py @@ -1,19 +1,28 @@ # ex.py class B(object): - def __init__(self, *args): - print "B.__init__" - super(B, self).__init__(*args) - -class M(B, type): - def __init__(self, n, b, d): - print "M.__init__" - super(M, self).__init__(n, b, d) - + def __repr__(self): + return '<instance of %s>' % self.__class__.__name__ + def meth(self): + print "B.meth(%s)" % self + meth = classmethod(meth) + class C(B): - __metaclass__ = M - def __init__(self): - print "C.__init__" - super(C, self).__init__() + def meth(self): + print "C.meth(%s)" % self + self.__super.meth() + meth = classmethod(meth) + +C._C__super = super(C) + +class D(C): + pass + +D._D__super = super(D) + + +d=D() + +d.meth() diff --git a/pypers/super/super2.txt b/pypers/super/super2.txt deleted file mode 100644 index 4458a01..0000000 --- a/pypers/super/super2.txt +++ /dev/null @@ -1,779 +0,0 @@ -Ten things to know about ``super`` -==================================== - -:Author: Michele Simionato -:Email: michele.simionato@gmail.com -:Initial Draft: June 2004 -:Last Revision: May 2008 -:Version: 0.6 - -``super`` is Python a built-in, first introduced in Python 2.2 and -slightly improved and fixed in later versions, which is often -misunderstood by the average Python programmer. One of the reasons for -that, is the poor documentation of ``super``: at the time of this -writing (May 2008) the documentation is incomplete and in some parts -misleading and even wrong. For instance, the standard documentation -(even for the new 2.6 version -http://docs.python.org/dev/library/functions.html#super) still says:: - - super(type[, object-or-type]) - Return the superclass of type. If the second argument is omitted the - super object returned is unbound. If the second argument is an object, - isinstance(obj, type) must be true. If the second argument is a type, - issubclass(type2, type) must be true. super() only works for new-style - classes. - -The first sentence is just plain wrong: ``super`` does not return the -superclass. There is no such a thing as "the" superclass in a Multiple -Inheritance (MI) world. Also, the sentence about *unbound* is misleading, -since it may easily lead the programmer to think about bound and unbound -methods, whereas it has nothing to do with that concept. Finally, there are -subtle pitfalls and dark corners of ``super`` which are not at all mentioned. -IMNSHO ``super`` is one of the most trickiest and surprising Python -constructs, and we absolutely needs a document to shed light on its secrets. -The present paper is a first step in this direction: it aims to tell you -the *truth* about ``super``. At least the amount of truth -I have discovered with my experimentations, which is certainly -not the whole truth ;) - -A fair warning is in order here: this document is aimed to expert -Pythonistas. It assumes you are familiar with new-style classes [#]_ -and the Method Resolution Order (MRO_) -concept; moreover a good understanding of descriptors_ would be extremely -useful in order to grasp this document. Some parts also require good -familiarity with metaclasses_. All in all, this paper is not for the faint -of heart ;) - -.. _MRO: http://www.python.org/download/releases/2.3/mro/ -.. _descriptors: http://users.rcn.com/python/download/Descriptor.htm -.. _metaclasses: http://www.ibm.com/developerworks/library/l-pymeta.html - -First thing: there is no superclass in a MI world ----------------------------------------------------------- - -Readers familiar will single inheritance languages, such as -Java or Smalltalk, will have a clear concept of superclass -in mind. This concept, however, has *no useful meaning* in Python or in -other multiple inheritance languages. I became convinced of this fact -after a discussion with Bjorn Pettersen and Alex Martelli -on comp.lang.python in May 2003 [#]_ -(at that time I was mistakenly thinking that one could define a -superclass concept in Python). Consider this example from that -discussion: - - :: - - +-----+ - | T | - |a = 0| - +-----+ - / \ - / \ - +-------+ +-------+ - | A | | B | - | | | a = 2 | - +-------+ +-------+ - \ / - \ / - +-----+ - | C | - +-----+ - : - : instantiation - c - ->>> class T(object): -... a = 0 - ->>> class A(T): -... pass - ->>> class B(T): -... a = 2 - ->>> class C(A,B): -... pass - ->>> c = C() - -Which is the superclass of ``C``? There are two direct superclasses (i.e. bases) -of ``C``: ``A`` and ``B``. ``A`` comes before ``B``, so one would naturally -think that the superclass of ``C`` is ``A``. Iif ``super(C,c)`` was returning -the superclass of ``C``, -then it should return ``A``. ``A`` inherits its attribute ``a`` from ``T``, -where ``a`` has the value 0, so ``super(C,c).a`` should return 0. This -is NOT what happens. Instead, ``super(C,c).a`` walks trought the -method resolution order of the class of ``c`` (i.e. ``C``) -and retrieves the attribute from the first class above ``C`` which -defines it. In this example the MRO of ``C`` is ``[C, A, B, T, object]``, so -``B`` is the first class above ``C`` which defines ``a`` and ``super(C,c).a`` -correctly returns the value 2, not 0: - ->>> super(C,c).a -2 - -You may call ``A`` the superclass of ``C``, but this is not an useful -concept since the methods are resolved by looking at the classes -in the MRO of ``C``, and not by looking at the classes in the MRO of ``A`` -(which in this case is ``[A,T, object]`` and does not contain ``B``). -The whole MRO is needed, not just the first superclass. - -So, using the word *superclass* in the standard doc is completely -misleading and should be avoided altogether. - -Second thing: ``super`` returns proxy objects ----------------------------------------------------- - -Having established that ``super`` cannot return and does not return the -mythical superclass, we may ask ourselves what the hell is returning -``super`` ;) The truth is that ``super`` returns proxy objects. - -Informally speaking, a proxy object is an object with -the ability to dispatch to methods of other classes via delegation. -Technically, ``super`` is a class overriding the ``__getattribute__`` -method. Instances of ``super`` are proxy objects providing -access to the methods in the MRO. The dispatch is done in such a way -that - -``super(cls, instance-or-subclass).meth(*args, **kw)`` - -corresponds to - -``right-method-in-the-MRO-applied-to(instance-or-subclass, *args, **kw)`` - -There is a caveat at this point: the second argument can be -an instance of the first argument, or a subclass of it, but -in *both cases* a bound method is returned (one could naively -think that when a subclass is passed one gets an unbound method). - -For instance, in this example - -.. code-block:: python - - >>> class B(object): - ... def __repr__(self): - ... return "<instance of %s>" % self.__class__.__name__ - - >>> class C(B): - ... pass - - >>> class D(C): - ... pass - - >>> d = D() - -both - -.. code-block:: python - - >>> print super(C,d).__repr__ - <bound method D.__repr__ of <instance of D>> - -and - - -.. code-block:: python - - >>> print super(C,D).__repr__ - <bound method D.__repr__ of <class 'D'>> - -returns bound methods. This means that when you call the ``__repr__`` methods, -you will get - - -.. code-block:: python - - >>> print super(C,d).__repr__() - <instance of D> - -(here ``d``, a ``D`` instance, is being passed to ``__repr__``) and - -.. code-block:: python - - >>> print super(C, D).__repr__() - <instance of type> - -(here ``D``, an instance of the (meta)class ``type``, is being passed -to ``__repr__``). - -Reading the docs, one could think that in order to get unbound methods, -one would need to switch to the alternative syntax of ``super``, the single -argument syntax. This is actually untrue. To understand how unbound -methods can be retrieved we need to talk about descriptors. - -Third thing: ``super`` returns descriptor objects ----------------------------------------------------- - -Descriptors (more properly I should speak of the descriptor protocol) were -introduced in Python 2.2 by Guido van Rossum. Their primary motivation -is technical, since they were needed to implement the new-style object -system. Descriptors were also used to introduce new standard concepts in -Python, such as classmethods, staticmethods and properties. Moreover, -according to the traditional transparency policy of Python, descriptors -were exposed to the application programmer, giving him/her the freedom -to write custom descriptors. Any serious Python programmer should have -a look at descriptors: luckily they are now very well documented (which was -not the case when I first studied them :-/) thanks to the beautiful essay -of Raimond Hettinger [#]_. You should read it before continuing this article, -since it explains all the details. However, for the sake of our discussion -of ``super``, it is enough to say that a *descriptor class* is just a -regular new-style class which implements a ``.__get__`` method with -signature ``__get__(self, obj, objtyp = None)``. A *descriptor object* -is just an instance of a descriptor class. - -Descriptor objects are intended to be used as attributes (hence their -complete name attribute descriptors). Suppose that ``descr`` is a -given descriptor object used as attribute of a given class C. -Then the syntax ``C.descr`` is actually interpreted by Python as a -call to ``descr.__get__(None, C)``, whereas the same syntax for an -instance of C corresponds to a call to ``descr.__get__(c, type(c))``. - -The unbound method ``__repr__`` can be retrieved as - -.. code-block:: python - - >>> super(C,d).__repr__.__get__(None,D) # or with D instead of d - <unbound method D.__repr__> - -and we may check that it works correctly: - -.. code-block:: python - - >>> print _(d) - <instance of D> - -This is cumbersome and tricky, but it is the only way to get -the unbound method. Using the unbound form of ``super`` does -*not* return ``D.__repr__``: instead it returns ``super.__repr__`` -bound to the (unbound) super object ``super(C)``: - -.. code-block:: python - - >>> print super(C).__repr__() # same as repr(super(C)) - <super: <class 'C'>, NULL> - -Very tricky. Notice that ``super`` also redefines ``__new__``, -``__init``, ``__get__``, ``__getattribute``, as well as inheriting -other special attributes from ``object``. So using the single-argument -syntax you will dispatch to -these methods in ``super`` and not to the right methods defined in -the hierarchy at hand. On the other hand, the two-argument syntax -does not have this problem. For instance - -.. code-block:: python - - >>> print super(C,C).__repr__() - <instance of type> - -does the right thing. - -This other example should shed further light. Suppose ``B`` -has a method called ``meth`` like this: - -.. code-block:: python - - >>> B.meth = lambda self :'You called B.meth with first argument %s' % self - -Then ``B.meth`` is an unbound method and, mislead by the documentation, -one could expect to be able to access it with the syntax ``super(C).meth``. -This is not the case. You get an error instead: - -.. code-block:: python - - >>> super(C).meth - Traceback (most recent call last): - ... - AttributeError: 'super' object has no attribute 'meth' - -Unbound super objects cannot be accessed directly, -they must be converted to bound objects in order to make them -to dispatch properly. Unbound super objects can be converted to -bound super objects via the descriptor protocol. For instance, -in this example I can convert ``super(C)`` in a super object -bound to ``d`` in this way: - -.. code-block:: python - - >>> boundsuper = super(C).__get__(d, D) # this is the same as super(C,d) - -Now I can access the bound method 'd.meth': - -.. code-block:: python - - >>> print boundsuper.meth - <bound method D.<lambda> of <instance of D>> - -As a consequence, ``__get__`` cannot be turned into a cooperative method -just by using ``super``: you would get the wrong ``get``. - -Fourth thing: the *unbound* syntax is a mess ------------------------------------------------------------------- - -Having established that the *unbound* syntax does not return unbound methods -one might ask what its purpose is. -The answer is that ``super(C)`` is intended to be used as an attribute in -other classes. Then the descriptor magic will automatically convert the -unbound syntax in the bound syntax. For instance: - -.. code-block:: python - - >>> class B(object): - ... a = 1 - >>> class C(B): - ... pass - >>> class D(C): - ... sup = super(C) - >>> d = D() - >>> d.sup.a - 1 - -This works since ``d.sup.a`` calls ``super(C).__get__(d,D).a`` which is -converted to ``super(C, d).a`` and retrieves ``B.a``. - -There is a single use case for the single argument -syntax of ``super`` that I am aware of, but I think it gives more troubles -than advantages. The use case is the implementation of *autosuper* made -by Guido on his essay about `new-style classes`_. - -.. _new-style classes: http://www.python.org/download/releases/2.2.3/descrintro/#cooperation - -The idea there is to use the unbound super objects as private -attributes. For instance, in our example, we could define the -private attribute ``__sup`` in the class ``C`` as the unbound -super object ``super(C)``: - -.. code-block:: python - - >>> C._C__sup = super(C) - -With this definition inside the methods the syntax -``self.__sup.meth(arg)`` can be used -as an alternative to ``super(C, self).meth(arg)``, and the advantage is -that you avoid to repeat the name of the class in the calling -syntax, since that name is hidden in the mangling mechanism of -private names. The creation of the ``__sup`` attributes can be hidden -in a metaclass and made automatic. So, all this seems to work: but -actually this *not* the case. - -Things may wrong in various case, for instance for classmethods, -as in this example: - -.. code-block:: python - - #<ex.py> - - class B(object): - def __repr__(self): - return '<instance of %s>' % self.__class__.__name__ - def meth(self): - print "B.meth(%s)" % self - meth = classmethod(meth) - - class C(B): - def meth(self): - print "C.meth(%s)" % self - self.__super.meth() - meth = classmethod(meth) - - C._C__super = super(C) - - class D(C): - pass - - D._D__super = super(D) - - - d=D() - - d.meth() - - #</ex.py> - -The last line raises an ``AttributeError: 'super' object has no attribute -'meth'.`` - -So, using a ``__super`` unbound super object is not a robust solution -(notice that everything would work by substituting ``self.__super.meth()`` -with ``super(C,self).meth()``. -In Python 3.0 all this has been resolved in a much better way. - -.. There are other ways to avoid repeating the class name, see for instance my cookbook recipe [#]_. - -If it was me, I would just remove the single argument syntax of ``super``, -making it illegal. But this would probably break someone code, so -I don't think it will ever happen in Python 2.X. Another solution would be just to -deprecate it. There is no need for this syntax, one can always circumvent -it. - -Fifth thing: ``super`` does not work with meta-attributes ----------------------------------------------------------------------- - -If you start using ``super`` intensively, soon or latter you will find -a number of subtilities. One of these is the fact that ``super`` does not -work well with the ``__name__`` special attribute. Consider this example: - -.. code-block:: python - - >>> class B(object): - ... "This is class B" - ... - >>> class C(B): - ... pass - ... - -Here the special (class) attribute ``__doc__`` is retrieved as you would expect: - -.. code-block:: python - - >>> super(C,C).__doc__ == super(C,C()).__doc__ == B.__doc__ - True - -On the other hand, the special attribute ``__name__`` is not -retrieved correctly: - -.. code-block:: python - - >>> super(C,C).__name__ # one would expect it to be 'B' - Traceback (most recent call last): - File "<stdin>", line 1, in ? - AttributeError: 'super' object has no attribute '__name__' - -The problem is that ``__name__`` is not just a plain class -attribute: it is actually a "getset descriptor" defined on -the metaclass "type" (try to run ``help(type.__dict__['__name__'])`` -and you will see for yourself). More in general, ``super`` has -problems with meta-attributes, i.e. class attributes of metaclasses. - -Meta-attributes differs from regular attributes since they are not -transmitted to the instances of the instances.You can find the -rationale for this behaviour elsewhere [#]_: here I am only interested -to the issues with ``super``. Consider this example: - -.. code-block:: python - - #<example1.py> - - class M(type): - "A metaclass with a class attribute 'a'." - a = 1 - - class B: - "An instance of M with a meta-attribute 'a'." - __metaclass__ = M - - class C(B): - "An instance of M with the same meta-attribute 'a'" - - if __name__ == "__main__": - print B.a, C.a # => 1 1 - print super(C,C).a #=> attribute error - - #</example1.py> - -If you run this, you will get an attribute error. This is a case -where ``super`` is doing the *right* thing, since 'a' is *not* inherited -from B, but it comes directly from the metaclass (again, look at my second -article with David Mertz to understand why it is so), so 'a' -is *not* in the MRO of C. A similar thing happens for the ``__name__`` -attribute (the fact that it is a descriptor and not a plain -attribute does not matter), so ``super`` is working correctly, but -still it may seems surprising at first. - -Sixth thing: special attribute access for special attributes is special ;) ----------------------------------------------------------------------------- - -There is also another subtle pitfall which is not directly related -to ``super`` but which is often encountered working with ``super``. -This came up at least three or four times in the newsgroup, and there -are various independent bug reports on sourceforge about it, so possibly -you may face it too. Bjorn Pettersen was the first one who pointed out the -problem to me (see also bug report SF 729913): the issue is that - -``super(MyCls, self).__getitem__(5)`` - -works, but not - -``super(MyCls, self)[5]``. - -The problem is general to all special methods, not only to ``__getitem__``, -and the explanation for that has to do with the implementation of -attribute lookup for special methods. Clear explanations of what is -going on are provided by Michael Hudson as a comment to the bug report: -SF789262 and by Raymond Hettinger as a comment to the bug report SF805304. -Shortly put, this is not a problem of ``super`` per se, the problem is -that the special call ``x[5]`` (using ``__getitem__`` as example) is -converted to ``type(x).__getitem__(x,5)`` -*only if* ``__getitem__`` is explicitely defined in ``type(x)``. If -``type(x)`` does not define ``__getitem__`` directly, but only -indirectly via delegation (i.e. overriding ``__getattribute__``), -then the second form works but not the first one. - -This restriction will likely stay in Python because it would involve -a really big change and a loss of performances, so it has to be -considered just a documentation bug, since nowhere in -the docs it is mentioned that special calling syntaxes (such as -the ``[]`` call, the ``iter`` call, the ``repr`` call, etc. etc.) -are special and bypass ``__getattribute__``. Guido advice is: -just use the more explicit form and everything will work. - -Finally, there may be other bugs and pitfalls I am not aware of. Certainly -there are many other issues and bugs in previous versions of Python that -I have not mentioned here, since they have been fixed, but that you may -encounter if you use Python 2.2 (and maybe even in 2.3). - -Seventh thing: mixing super and non-super using methods is tricky --------------------------------------------------------------------- - -Some years ago James Knight wrote an eassay titled -`Super considered harmful`_ [#]_ where he points out a few shortcomings -of ``super`` and makes an important recommendation: -*use super consistently, and document that you use it, as it is part of the -external interface for your class, like it or not*. -The issue is that a developer inheriting from a hierarchy written by somebody -else, has to know if the hierarchy author has used ``super`` internally -or not. For instance, consider this case, where the library author has -used ``super`` internally: - -.. code-block:: python - - class A(object): - def __init__(self): - print "A", - super(A, self).__init__() - - class B(object): - def __init__(self): - print "B", - super(B, self).__init__() - -If the application programmers knows that the library uses ``super`` internally, -he will use ``super`` and everything will work just fine; but it he does not -know if the library uses ``super`` he may be tempted to call ``A.__init__`` -and ``B.__init__`` directly, but this will end up in having ``B.__init__`` -called twice! - -.. code-block:: python - - class C(A,B): - def __init__(self): - print "C", - A.__init__(self) - B.__init__(self) - - # c = C() will print - -On the other hand, if the library does not uses ``super`` internally, - -.. code-block:: python - - class A(object): - def __init__(self): - print "A", - - class B(object): - def __init__(self): - print "B", - -the application programmer cannot use ``super`` either, otherwise -``B.__init__`` will not be called: - -.. code-block:: python - - class C(A,B): - def __init__(self): - print "C", - super(C, self).__init__() - - # c = C() will print - -So, if use a library featuring multiple inheritance, you must know if the -hierarchy was intended to be cooperative (using ``super``) or not. - -Eighth thing: argument passing in cooperative methods can fool you ----------------------------------------------------------------------- - -James Knight devolves a paragraph to the discussion of argument passing -in cooperative methods. Basically, if you want to be safe, all your cooperative -methods should have a compatible signature. There are various ways to -get a compatible signature, for instance you could accept everything (i.e. -your cooperative methods could have signature ``*args, **kw``) which is -a bit too much for me, or all of your methods could have exactly the same -arguments. The issue comes when you have default arguments, since your -MRO can change if you change your hierarchy, and argument passing may -break down. Here is one example - -.. code-block:: python - - class A(object): - def __init__(self): - print 'A' - - class B(object): - def __init__(self, a=None): - print 'B with a=%s' % a - super(B, self).__init__(a) - - class C(object): - def __init__(self, a): - print 'C with a=%s' % a - super(C, self).__init__() - - class D(B, C): - def __init__(self): - print 'D' - super(D, self).__init__() - - d = D() - -This works, but it is fragile (you see what will happen if you change -``D(B,C)`` with ``D(C,B)``?) and in general it is always difficult -to figure out which arguments will be passed to each method and in -which order (the order changing if you change the hierarchy) so it is -best just to use the same arguments everywhere (or not to use -cooperative methods altogether, if you have no need for cooperation). -There is no shortage of examples of trickiness in multiple inheritance -hierarchy; for instance I remember a post from comp.lang.python [#]_ -. Also, beware of situations in which you have -some old style classes mixing with new style classes: the result may -depend on the order of the base classes (see examples 2-2b and 2-3b -in `Super considered harmful`_. - -.. _Super considered harmful: http://fuhm.net/super-harmful/ - -Ninth thing: ``super`` had bugs in earlier versions of Python ------------------------------------------------------------------ - -There are various ``super`` pitfalls currently undocumented of which -the experienced Python programmer should be aware of. - -The unbound form of ``super`` does not play well with pydoc. -The problems is still there in Python 2.3.4 (see bug report SF729103) - -.. code-block:: python - - >>> class B(object): pass - ... - >>> class C(B): - ... s=super(B) - ... - >>> help(C) - Traceback (most recent call last): - ... - ... lots of stuff here - ... - File "/usr/lib/python2.3/pydoc.py", line 1198, in docother - chop = maxlen - len(line) - TypeError: unsupported operand type(s) for -: 'type' and 'int' - -I have not yet clear what the cause is, but it is certainly quite -tricky. An incompatibility between ``super`` was reported by Christian Tanzer -(SF902628); if you run the following, you will get a TypeError: - -.. code-block:: python - - #<ex.py> - - class C(object): - pass - - C.s = super(C) - - if __name__ == "__main__": - import doctest, __main__ - doctest.testmod(__main__) - - #<ex.py> - -BTW, I don't think this is related to ``super`` only since I have -found similar problems when playing with descriptors and doctest -some time ago (but I cannot reproduce the bug right now). - -Tenth and last thing: when it comes to ``super`` don't trust even Guido himself! ----------------------------------------------------------------------------------- - -In order to explain how ``super`` works, Guido describes a -"fully functional implementation of the super() built-in class in -pure Python" in "Unifying types and classes in Python 2.2". -Unfortunately, that implementation is more harmful than helpful, since -the current ``super`` DOES NOT work in the same way :-( -Take for instance this example: - -.. code-block:: python - - >>> class C(object): - ... f='C.f' - >>> class D(C): - ... f='D.f' - -Here the ``super`` works fine, - -.. code-block:: python - - >>> print super(D,D()).f - C.f - -but the class ``Super`` described by Guido will raise an attribute -error when invoked as ``Super(D,D()).f``. Therefore ``Super`` is NOT -equivalent to the currently implemented ``super`` built-in. - -Conclusion: is there life beyond super? -------------------------------------------------------- - -In this paper I have argued that ``super`` is tricky, -however the existence of dark corners is not a compelling argument against -a language construct: after all, they are rare and there is an easy solution to -their obscurity, i.e. documenting them. -Also, even from experience I have come to believe that cooperative -methods are a wart (it is too difficult to reason about them, they are -fragile, and overall I see them as an unneeded complication in 99.9% -of the cases) they have their uses in a multiple inheritance world, as -Guido argues successfully in his essay (the canonical example being a -diamond diagram with ``.save`` methods which must be called -cooperatively). -The problem therefore is not with ``super`` or with cooperative -methods, the problem is with multiple inheritance itself (*if the -implementation is hard to explain, it's a bad idea*). - -I personally liked super, cooperative methods and multiple inheritance -for a couple of years, then I started working with Zope and my mind -changed completely. The problem is multiple inheritance itself: after -all Zope 2 did not use super at all but is a mess anyway. Inheritance -makes your code heavily coupled and difficult to follow (*spaghetti -inheritance*). I have not found a real life problem yet that I could -not solve with single inheritance + composition/delegation in a better -and more maintainable way than using multiple inheritance. -Nowadays I use multiple inheritance for quick and dirty hacks, -but never for designing new hierarchies from scratch. -There are alternative to multiple inheritance: for instance Ruby uses -mixins (they are a restricted multiple inheritance without cooperative -methods and with a well defined superclass, but they do not solve -the issue of name conflicts and the issue with the ordering of -the mixin classes); recently some people proposed the concepts -of traits (restricted mixin where name conflicts must be solved explicitely -and the ordering of the mixins does not matter) which is interesting. -In CLOS multiple inheritance works better since (multi-)methods -are defined outside classes and ``call-next-method`` is well integrated -in the language; -it is simpler to track down the ancestors -of a single method than to wonder about the full class hierarchy. -The language SML (which nobody except academics use, but would deserve -better recognition) goes boldly in the direction of favoring composition over -inheritance and uses functors to this aim. This is an approach I like very -much on the paper, but I lack real experience in a large code base to judge -merits and demerits of this approach. - -Notes ------------------ - -.. [#] http://www.python.org/download/releases/2.2.3/descrintro/ -.. [#] I wrote an essay on Python 2.3 Method Resolution Order which - you may find here: http://www.python.org/download/releases/2.3/mro/ -.. [#] Raymond Hetting wrote a beautiful essay on descriptors: http://users.rcn.com/python/download/Descriptor.htm -.. [#] David Mertz and me wrote a trilogy of articles on metaclasses; - the second one is the relevant one for the issues discussed here: - http://www-128.ibm.com/developerworks/linux/library/l-pymeta2/ -.. [#] a thread proving that there is no useful concept of superclass - http://tinyurl.com/5ms8lk -.. [#] Python's Super is nifty, but you can't use it, http://fuhm.net/super-harmful -.. [#] A thread about subtle cooperative hierarchies http://tinyurl.com/3jqhx7 -.. [#] Traits: http://www.iam.unibe.ch/~scg/Research/Traits/ |