summaryrefslogtreecommitdiff
path: root/artima
diff options
context:
space:
mode:
authormichele.simionato <devnull@localhost>2009-04-03 05:25:57 +0000
committermichele.simionato <devnull@localhost>2009-04-03 05:25:57 +0000
commit858f5588c7e8ecf8cb436f672c0db0eb579a5f7c (patch)
tree401a7ad58f758e5befdd8fea30fde16d7856f875 /artima
parentbf2b0e02a0a4c5bcda96706d12d8eea62642da2a (diff)
downloadmicheles-858f5588c7e8ecf8cb436f672c0db0eb579a5f7c.tar.gz
Published version of mixins3
Diffstat (limited to 'artima')
-rw-r--r--artima/python/Makefile4
-rw-r--r--artima/python/box.jpgbin0 -> 16107 bytes
-rw-r--r--artima/python/diavolo.pngbin0 -> 37037 bytes
-rw-r--r--artima/python/mixins3.py163
-rw-r--r--artima/python/mixins4.py78
5 files changed, 132 insertions, 113 deletions
diff --git a/artima/python/Makefile b/artima/python/Makefile
index 6c31eca..d4c5273 100644
--- a/artima/python/Makefile
+++ b/artima/python/Makefile
@@ -37,5 +37,9 @@ mixins1: mixins1.py
mixins2: mixins2.py
$(MINIDOC) -d mixins2; $(POST) /tmp/mixins2.rst 246483
+
+mixins3: mixins3.py
+ $(MINIDOC) -d mixins3; $(POST) /tmp/mixins3.rst 254367
+
traits: traits.txt
$(POST) traits.txt 246488
diff --git a/artima/python/box.jpg b/artima/python/box.jpg
new file mode 100644
index 0000000..5ab1497
--- /dev/null
+++ b/artima/python/box.jpg
Binary files differ
diff --git a/artima/python/diavolo.png b/artima/python/diavolo.png
new file mode 100644
index 0000000..e1e951a
--- /dev/null
+++ b/artima/python/diavolo.png
Binary files differ
diff --git a/artima/python/mixins3.py b/artima/python/mixins3.py
index 9452de2..b303af2 100644
--- a/artima/python/mixins3.py
+++ b/artima/python/mixins3.py
@@ -6,11 +6,11 @@
.. _ABC: http://www.python.org/dev/peps/pep-3119/
.. _strait: http://pypi.python.org/pypi/strait
-What attitude to keep towards multiple inheritance and mixins
+Three attitudes
-------------------------------------------------------------------------------
In recent years I have become an opponent of multiple inheritance and
-mixins, for reason which I have discussed at length in the
+mixins, for reasons which I have discussed at length in the
first_ and second_ paper of this series: namespace pollution,
insufficient separation of concerns, fragility with respect to name
clashes, complication of the method resolution order, non scalability
@@ -18,22 +18,22 @@ of the design and even simple conceptual confusion with the *is a*
relation.
Moreover, I have argued that mixins are the wrong tool for the job: if
-you need a method to be mixed in different classes, then you are
+you need a method to be mixed into different classes, then you are
better off not mixing it, putting it at toplevel and promoting it to a
multimethod_/`generic function`_.
Nevertheless, a lot of people think that mixins are very
cool and a lot of new languages present mixins as the panacea
to all evil in object oriented design. This is the reason why
-I have started this series, which is intended for developers using
-languages which features multiple inheritance or mixins and not
-only for Python developers, even if all of my examples here
-are in Python, since I am primarily a Python developer.
+I have started this series, which is intended to make developers using
+languages which features multiple inheritance or mixins think.
In my view there are at least three possible attitudes
-for a developer using a language with mixins:
+for a developer using a language with mixins (say Python):
-1. Resignation. Acknowledge that since the language allows mixins and
+1. Resignation
+
+ Acknowledge that since the language allows mixins and
they are used by many frameworks, they will never go away.
Therefore one should focus on discovering workarounds to cope
with the situation, like the ``warn_overriding`` decorator that I
@@ -41,12 +41,16 @@ for a developer using a language with mixins:
better introspection tools to navigate though mixins (the issue
with pydoc is that it give *too much* information);
-2. Education. We (the "experts") should make an effort to communicate
+2. Education
+
+ We (the "experts") should make an effort to communicate
to the large public the issues with mixins and try to convince framework
- authors to use alternative design. That is what I am trying to accomplish
+ authors to use alternative designs. That is what I am trying to accomplish
with this series.
-3. Research. Study better implementations of the mixin idea: even if there
+3. Research
+
+ Study better implementations of the mixin idea: even if there
is no hope for the language you are using, research is not useless
since it may be implemented in languages yet to be written.
Python itself can be used as an experimentation language, as I show
@@ -54,16 +58,16 @@ for a developer using a language with mixins:
become a single inheritance + traits object system, instead of
a multiple inheritance system. In the fourth paper of this series
I will show yet another approach, by implementing the mixin idea
- in terms of descriptors and not of inheritance.
+ in terms of composition and not of inheritance.
-Ways to avoid multiple inheritance
+Avoiding multiple inheritance with proxies
------------------------------------------------------------------
While I think that multimethods are the right way to solve the problem
-that mixins are tring to solve, the multiple dispatch solution of defining
-functions outside classes is a radical departure from the traditional
-style of object oriented programming, which is based on single
-dispatch and methods defined inside classes.
+that mixins are tring to solve, the solution of defining functions
+outside classes and leveraging on multiple dispatch is a radical
+departure from the traditional style of object oriented programming,
+which is based on single dispatch and methods defined inside classes.
If you want to keep single dispatch, then there is basically only one
way to avoid inheritance, i.e. to replace it with *composition*,
@@ -75,71 +79,80 @@ The strait_ module for instance just inject methods in a class
directly (provided they satisfy some checks) and as such it is a not
a big improvements with respect to inheritance: the advantages are in
the protection against name clashes and in the simplication of the
-method resolution order. However, one could still argue that those
+method resolution order. However, one could argue that those
advantages are not enough, since the namespace pollution
problem is still there.
An alternative solution is to use composition plus delegation, i.e.
-to make use of proxies. The cognitive load of a proxy - an object
-dispatching to another method - is much smaller than the cognitive
-load imposed by inheritance.
-
-If I see an object which is an instance of a class, I feel obliged to
-know all the methods of that class, including all the methods of its
-ancestors, because I could override them accidentally. On the other
-hand, if an object is a proxy, I just note in my mind that it contains
-a reference to the proxied object, but I do not feel obliged to know
-everything about the proxied object; it is enough for me to know
-which methods are called by the proxy methods, if any. It is more
-of a psychological effect, but I like proxies since they keep the
-complexity confined, whereas inheritance exposes it directly.
-
-In particular, if the proxy has a method which accidentally shadows a
-method of the underlying object, nothing particularly bad happens.
-That method will not work, but the other methods will keep working,
-since they will call the methods of the original object. In
-inheritance instead, an accidental overriding may cause havoc, since
-other methods of the object may call the accidentally overridden
-method, with errors appearing in apparently unrelated portions of
-code.
-
-Just to give a concrete example, in the case discussed in the second_
+to make use of proxies. For instance, in the case discussed in the second_
paper of this series, we could have solved the design problem without
using multiple inheritance, just with composition + delegation:
$$PictureContainer
-Thanks to the ``__getattr__`` trick, all the methods of
-``SimplePictureContainer`` are available to ``PictureContainer2``,
+Thanks to the ``__getattr__`` trick, ``PictureContainer`` is now
+a proxy to a ``SimplePictureContainer`` and all the methods of
+``SimplePictureContainer`` are available to ``PictureContainer``,
on top of the methods coming from ``DictMixin``: we did basically
fake multiple inheritance without complicating the hierarchy.
-A disadvantage of ``PictureContainer2`` is that its instances are no
+.. image:: box.jpg
+
+A disadvantage of ``PictureContainer`` is that its instances are no
more instances of ``SimplePictureContainer``, therefore if your code
contained checks like ``isinstance(obj, SimplePictureContainer)``
(which is a very bad practice, at least for Python versions below Python 2.6)
the check would fail. The problem has been solved in
Python 2.6 thanks to the Abstract Base Class mechanism (ABC_);
it is enought to register ``SimplePictureContainer`` as an ABC of
-``PictureContainer2`` and you are done.
+``PictureContainer`` and you are done.
-An exercise in design
--------------------------------------------------------------
+I like proxies because the cognitive load of a proxy - an object
+dispatching to another method - is much smaller than the cognitive
+load imposed by inheritance.
-.. image:: box.jpg
+If I see an object which is an instance of a class, I feel obliged to
+know all the methods of that class, including all the methods of its
+ancestors, because I could override them accidentally.
+
+On the other hand, if an object is a proxy, I just note in my mind
+that it contains a reference to the proxied object, but I do not feel
+obliged to know everything about the proxied object; it is enough for
+me to know which methods are called by the proxy methods, if any. It
+is more of a psychological effect, but I like proxies since they keep
+the complexity confined, whereas inheritance exposes it directly.
+
+I should notice that if the proxy has a method which accidentally shadows a
+method of the underlying object, nothing particularly bad happens.
+That method will not work, but the other methods will keep working,
+since they will call the methods of the original object. In
+inheritance instead, an accidental overriding may cause havoc, since
+other methods of the object may call the accidentally overridden
+method, with errors appearing in apparently unrelated portions of
+code.
-Since I do not like to talk in abstract, let me consider a concrete
-design problem, which I will solve by using mixin classes but without
-incurring in the overpopulation issue. To this aim, let me refer back
-to the `second`_ article in this series, specifically to the class
-``PictureContainer2`` which inherits from``DictMixin``.
-As I said, deriving from ``DictMixin`` is good since ``DictMixin``
-provided only 19 attributes (you can see them with ``dir(DictMixin)``)
-which are well known to everybody knowing Python dictionaries, therefore
-the cognitive load is null.
+An exercise in design
+-------------------------------------------------------------
+As well as I like proxies, they are not perfect. I can see two
+problems with them: they slowdown attribute access quite a lot (this a
+performance problem, therefore not something that should concern a
+Pythonista a lot) and they are pretty opaque, since they hide the
+underlying object quite a lot (this is a feature, of course, but
+sometimes you would prefer to be more explicit). Moreover, if you have
+a complex problem, you do not want to solve it *onion-style*, by using
+proxies to proxies to proxies.
+
+Since I do not like to talk in abstract, let me refer to the
+``PictureContainer`` example again.
+
+The class ``PictureContainer`` inherits from ``DictMixin`` and I said that
+this is good since ``DictMixin`` provided only 19 attributes (you can see
+them with ``dir(DictMixin)``) which are well known to everybody
+knowing Python dictionaries, therefore the cognitive load is null.
The problem begins when you decide that you need to add features to
-``PictureContainer2``.
+``PictureContainer``.
+
For instance, if we are writing a GUI application, we may need methods
such as ``set_thumbnails_size, get_thumbnails_size,
generate_thumbnails, show_thumbnails, make_picture_menu, show_picture_menu``,
@@ -178,14 +191,19 @@ support yet another interface and more mixin methods. In my estimate
I have been conservative, but it is easy to reach hundreds of
methods. This scenario is exactly what happened in Zope/Plone.
-In such a situation one must ask if there are alternative designs that
-would avoid the overpopulation problem. The answer is yes, and it is the
-standard one: *use composition instead of inheritance*. Everybody recommends
-this practice, but yet it is not followed enough in real life.
+It is clear that making a six level proxy of proxy wrapping a set of methods
+over the other is not a better solution than using mixins. Using traits
+would not be better either.
+One must ask if there are alternative designs that
+avoid the overpopulation problem. The answer is yes, of course,
+but in order to keep the suspence up, I will not show any solution
+to this design problem in this issue. I leave it for my next
+(and last) paper on the subject, so that you have some time to come
+up with a solution of your own ;)
+
+.. image:: diavolo.png
-.. _from Aristotle's times: http://en.wikipedia.org/wiki/Categories_(Aristotle)
-.. _articolo precedente: mixins1.html
-.. _Plone Site: http://www.phyast.pitt.edu/~micheles/python/plone-hierarchy.png
+*to be continued ...*
"""
from UserDict import DictMixin
@@ -193,6 +211,7 @@ from mixins2 import Picture, SimplePictureContainer
from datetime import datetime
class PictureContainer(DictMixin):
+ "PictureContainer is a proxy to the underlying SimplePictureContainer"
from utility import log
def __init__(self, id, pictures_or_containers):
@@ -216,16 +235,6 @@ class PictureContainer(DictMixin):
def __getattr__(self, name):
return getattr(self._pc, name)
-# def subclass(cls, base):
-# bases = cls.__bases__
-# if bases and bases != (object,):
-# raise TypeError('Nontrivial base classes %s' % bases)
-# return type(cls.__name__, (base, object), vars(cls).copy())
-
-# class DLPictureContainer(subclass(SimplePictureContainer, DictMixin)):
-# pass
-
-#help(DLPictureContainer)
p1 = Picture('pic00001', '/home/micheles/mypictures/pic00001',
"Michele al mare", datetime(2008, 06, 10))
diff --git a/artima/python/mixins4.py b/artima/python/mixins4.py
index 4b83daa..b7a5a20 100644
--- a/artima/python/mixins4.py
+++ b/artima/python/mixins4.py
@@ -1,38 +1,20 @@
+'''
Una possibile soluzione
---------------------------------------------------------------
-La mia soluzione, come anticipato nell'`articolo precedente`_ sarà
-quella di sostituire l'ereditarietà con la composizione + delegazione,
-ovverossia fare uso di oggetti proxy.
-Il carico cognitivo richiesto da un proxy - un
-oggetto che fa dispatch su di un altro oggetto - è molto
-inferiore al carico cognitivo imposto dall'ereditarietà.
-
-Se un oggetto è un' instanza di una classe, mi sento obbligato
-a conoscere tutti i suoi metodi (inclusi quelli di tutti i suoi
-antenati) se non altro perché potrei sovrascriverli accidentalmente
-mentre se un oggetto è un proxy mi basta sapere qual è
-l'oggetto a cui si riferisce,
-so che se voglio posso andare a vedere i metodi dell'oggetto,
-ma non mi sento obbligato a farlo.
-È più un motivo psicologico che altro, ma il proxy piace perché
-permette di tenere confinata la complessità, mentre l'ereditarietà
-la espone direttamente.
-
-Quest'ultimo punto è estremamente importante. Il cervello
-umano può memorizzare un numero limitato di cose. Un
-oggetto con dieci metodi può essere memorizzato abbastanza agevolmente
-mentre un oggetto con cento metodi esce dalle capacità del programmatore
-medio. La soluzione è quella di dividire i cento metodi in dieci categorie
-con dieci metodi ognuna: a questo punto le dieci categorie possono essere
-tenute in mente. La soluzione gerarchica scala: se avessi bisogno di
-mille metodi, basta definire dieci macro-categorie, ognuna contenente
-dieci categorie semplici, e tenere a mente le macro-categorie. La
-catalogazione gerarchica è la maniera naturale per la mente umana
-per memorizzare l'informazione, come formalizzato per lo meno `dai tempi
-di Aristotele`_, quindi è questa la cosa giusta
-da fare, non tenere i mille metodi tutti sullo stesso piano nello stesso
-namespace.
+This latter point is extremely important. The human brain can memorize
+a limited amount of information. An object with ten methods can be memorized
+easily enough, but an object with a hundred methods is outside the reach
+of the average programmer. The solution is to split the hundred methods
+into ten categories with ten methods each: at this point you can keep
+the ten categories in your mind. This hierarchical solution scales
+well: if I needed a thousand methods, I would just define ten macro-categories,
+each macro-category including ten micro-categories, and I would keep in
+mind the macro-categories. Hierarchical catalogs are the natural way
+to memorize information for the human mind, at least from
+`Aristotle's times`_: this is the right solution, not to have
+a thousand methods at the same level in the same namespace.
+
Nel caso in esame, ho deciso di trasformare tutte le classi di mixin in
proxy: se un attributo non viene trovato nel namespace del mixin, viene
@@ -110,8 +92,7 @@ Vedrete che l'autocompletamento funziona perfettamente
pc.ftp.SEND
...
-che l'help
-non vi sommerge di informazioni inutili
+che l'help non vi sommerge di informazioni inutili
.. code-block:: python
@@ -151,7 +132,32 @@ più curiosi l'implementazione del ``dispatcher``::
$$mdispatcher
-.. _dai tempi di Aristotele: http://it.wikipedia.org/w/index.php?title=Categoria_aristotelica
+Conclusion
+----------------------------------------
+
+My position is that mixins should be considered more
+of a hack than a legitimate design technique: they may be useful
+when you need to integrate with pre-existing
+code with a minimal offert, or as a debugging tool, when you want to
+instrument a third party hierarchy, but if are designing an application
+from scratch you are often better off if you do not rely on mixins.
+Recent versions of Python make attractive many alternatives to inheritance
+and I would say that the general trend of modern frameworks is to favor
+`component programming`_ rather than inheritance. You should take in
+account this fact. You should also take in account the fact that the
+problems of mixin programming become visible only when programming in
+the large, so you will find them only when your application will grow
+out of control. In the next post I will discuss how to avoid mixins
+in large frameworks. The solution is always the same: you should use
+*composition instead of inheritance* and you should *keep separated
+namespaces separated*.
+
+.. _ABC: http://www.python.org/dev/peps/pep-3119/
+.. _component programming: http://www.muthukadan.net/docs/zca.html
+
+It is a useful for quick and dirty hack, but I advice against starting with a
+design based on multiple inheritance for new code; actually, I usually
+recommend to use as little as possible even single inheritance!
'''
import os, copy, mdispatcher
@@ -293,7 +299,7 @@ if __name__ == '__main__': # test
c = C()
print c.m.add1(2)
-'''
+
Parafrasando il celebre articolo di Dijkstra_ del 1969
`Goto Considered Harmful`_, potremmo titolare questa parte
*Mixins Considered Harmful*. Notate che una ricerca con Google