{ "info": { "author": "Michele Simionato", "author_email": "michele.simionato@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", "Topic :: Software Development :: Libraries" ], "description": "A simple implementation of traits for Python\n==================================================================\n\n:Author: Michele Simionato\n:Date: 2015-08-16\n:Version: 0.5.3\n:Download: http://pypi.python.org/pypi/strait\n:Licence: BSD\n:Abstract: \n\n *I provide a simple implementation of \n traits as units of composable behavior for Python. I \n argue that traits are better than multiple inheritance.\n Implementing frameworks based on traits is left as an exercise\n for the reader.*\n\nMotivation\n------------------------------------------------\n\nMultiple inheritance is a hotly debated topic. \nThe supporters of multiple inheritance \nclaim that it makes code shorter and easier\nto read, whereas the opposers claim that is makes\ncode more coupled and more difficult to understand. I have\nspent some time in the past facing the intricacies of `multiple\ninheritance in Python`_ and I was one of its supporters once; however,\nsince then I have worked with frameworks making\nlarge use of multiple inheritance (I mean Zope 2) and nowadays I am in\nthe number of the people who oppose it. Therefore I\nam interested in alternatives.\n\nIn recent years, the approach\nof traits_ has gained some traction in a few circles and I have\ndecided to write a library to implement traits in Python, for\nexperimentation purposes. The library is meant for framework builders,\npeople who are thinking about writing a framework based on multiple\ninheritance - typically via the common mixin approach - but\nare not convinced that this is the best solution and would like to try\nan alternative. This library is also for authors of mixin-bases frameworks\nwhich are unsatisfied and would like to convert their\nframework to traits.\n\nAre traits a better solution than multiple inheritance and mixins? In\ntheory I think so, otherwise I would not have written this library, but\nin practice (as always) things may be different. It may well be \nthat using traits or using mixins does not make a big\ndifference in practice\nand that the change of paradigm is not worth the effort; or the\nopposite may be true. The only way to know is to try, to build\nsoftware based on traits and to see how it scale *in the large*.\nIn the small, more or less any approach works fine: it is only\nby programming in the large that you can see the differences. \n\nThis is\nthe reason why I am releasing this library with a liberal licence, so\nthat people can try it out and see how it works. The library is meant\nto play well (when possible) with pre-existing frameworks. \nAs an example, I will show\nhere how you could rewrite Tkinter classes to use traits instead of mixins. Of\ncourse, I am not advocating rewriting Tkinter: it would be silly \nand pointless; but it may have sense (or\nnot) to rewrite your own framework using traits, perhaps a framework\nwhich is used in house but has not been released yet.\n\nI am not the only one to have\nimplemented traits for Python; after finishing my implementation\nI made a little research and discovered a few implementations. Then\nI have also discovered the `Enthought Traits`_ framework, which however \nseems to use the name to intend something completely\ndifferent (i.e. a sort of type checking). My implementation has no\ndependencies, is short and I am committed\nto keep it short even in the future, according to\nthe principle of `less is more`_. \n\nThere is also an hidden agenda behind this module: to popularize some\nadvanced features of the Python object model which are little\nknown. The ``strait`` module is actually a tribute to the\nmetaprogramming capabilities of Python: such features are usually\nassociated to languages with a strong academic tradition - Smalltalk,\nScheme, Lisp - but actually the Python object model is no less\npowerful. For instance, changing the object system from a multiple\ninheritance one to a trait-based one, \ncan be done *within* the fundamental object system. The reason is that\nthe features that Guido used to implement the object system (special\nmethod hooks, descriptors, metaclasses) are there, available to the\nend user to build her own object system.\n\nSuch features are usually little used in the Python community, for\nmany good reasons: most people feel that the object system is good\nenough and that there is no reason to change it; moreover there is\na strong opposition to change the language, because Python programmers\nbelieve in uniformity and in using common idioms; finally, it is\ndifficult for an application programmer to find a domain where these\nfeatures are useful. An exception is the domain of the Object Relation\nMappers, whereas the Python language is often stretched to mimic the\nSQL language, a famous example of this tendency being SQLAlchemy_).\nStill, I have never seen a perversion of the object model as big as\nthe one implemented in the ``strait`` module, so I wanted to be the\nfirst one to perform that kind of abuse ;)\n\n.. _multiple inheritance in Python: MRO\n.. _less is more: http://www.artima.com/weblogs/viewpost.jsp?thread=236286\n.. _Enthought Traits: https://svn.enthought.com/enthought/wiki/Traits\n\nWhat are traits?\n------------------------------------------------------------\n\nThe word *traits* has many meanings; I will refer to it in the sense\nof the paper `Traits - Composable Units of Behavior`_ which implements\nthem in Squeak/Smalltalk. The paper appeared in 2003, but most of the\nideas underlying traits have been floating around for at least 30\nyears. There is also a trait implementation for `PLT Scheme`_ which is\nsomewhat close in spirit (if not in practice) to what I am advocating here.\nThe library you are reading about is by no means intended as a porting\nof the Smalltalk library: I am just stealing some of the ideas from\nthat paper to implement a Pythonic alternative to mixins which, for\nlack of a better name, I have decided to call traits. I feel no\nobligation whatsoever to be consistent with the Smalltalk library. In\ndoing so, I am following a long tradition, since a lot of languages\nuse the name *traits* to mean something completely different from the\nSmalltalk meaning. For instance the languages Fortress and Scala use\nthe name *trait* but with a different meaning (Scala traits are very\nclose to multiple inheritance).\nFor me a trait is a bunch of methods and attributes with the following\nproperties:\n\n1. the methods/attributes in a trait belong logically together;\n2. if a trait enhances a class, then all subclasses are enhanced too;\n3. if a trait has methods in common with the class, then the\n methods defined in the class have the precedence;\n4. the trait order is not important, i.e. enhancing a class \n first with trait T1 and then with trait T2 or viceversa is the same;\n5. if traits T1 and T2 have names in common, enhancing a class both\n with T1 and T2 raises an error;\n6. if a trait has methods in common with the base class, then the\n trait methods have the precedence;\n7. a class can be seen both as a composition of traits and as an homogeneous\n entity.\n\nProperties from 4 to 7 are the distinguishing properties of traits\nwith respect to multiple inheritance and mixins. In particular,\nbecause of 4 and 5, all the complications with the Method Resolution\nOrder disappear and the overriding is never implicit. Property 6 is\nmostly unusual: typically in Python the base class has the precedence\nover mixin classes. Property 7 should be intended in the sense that a\ntrait implementation must provide introspection facilities to make\nseemless the transition between classes viewed as atomic entities and\nas composed entities.\n\nA hands-on example\n------------------------------------------------------\n\nLet me begin by showing how you could rewrite a\nTkinter class to use traits instead of mixins. Consider the\n``Tkinter.Widget`` class, which is derived by the base class\n``BaseWidget`` and the mixin classes\n``Tkinter.Grid``, ``Tkinter.Pack`` and ``Tkinter.Place``: I want to\nrewrite it by using traits. The ``strait`` module\nprovides a factory function named ``include`` that does the job.\nIt is enough to replace the multiple inheritance syntax:\n\n.. code:: python\n\n class Widget(BaseWidget, Grid, Pack, Place):\n pass\n\nwith the following syntax:\n\n.. code:: python\n\n class Widget(BaseWidget):\n __metaclass__ = include(Pack, Place, Grid)\n\nI said that the conversion from mixins to traits was easy: but actually\nI lied since if you try to execute the code I just wrote you will\nget an ``OverridingError``:\n\n.. code:: python\n\n >>> from Tkinter import *\n >>> class Widget(BaseWidget):\n ... __metaclass__ = include(Pack, Place, Grid)\n Traceback (most recent call last):\n ...\n OverridingError: Pack overrides names in Place: {info, config, configure, slaves, forget}\n\nThe reason for the error is clear: both ``Pack`` and ``Place`` provide\nmethods called ``{info, config, configure, slaves, forget}`` \nand the traits implementation cannot figure out\nwhich ones to use. This is a feature, since it forces you to be\nexplicit. In this case, if we want to be consistent with\nmultiple inheritance rules, we want the methods coming from\nthe first class (i.e. ``Pack``) to take precedence. That can be\nimplemented by including directly those methods in the class namespace\nand relying on rule 3:\n\n.. code:: python\n\n class TOSWidget(BaseWidget):\n __metaclass__ = include(Pack, Place, Grid)\n info = Pack.info.im_func\n config = Pack.config.im_func\n configure = Pack.configure.im_func\n slaves = Pack.slaves.im_func\n forget = Pack.forget.im_func\n propagate = Pack.propagate.im_func\n\n\nNotice that we had to specify the ``propagate`` method too, since\nit is a common method between ``Pack`` and ``Grid``.\n\nYou can check that the TOSWidget class works, for instance by defining a\nlabel widget as follows (remember that ``TOSWidget`` inherits its signature\nfrom ``BaseWidget``):\n\n.. code:: python\n\n >>> label = TOSWidget(master=None, widgetName='label',\n ... cnf=dict(text=\"hello\"))\n\nYou may visualize the widget by calling the ``.pack`` method:\n\n.. code:: python\n\n >>> label.pack()\n\nThis should open a small window with the message \"hello\" inside it.\n\nA few caveats and warnings\n----------------------------------------------------\n\nFirst of all, let me notice that, in spite of apparency, ``include``\ndoes not return a metaclass. Insted, it returns a class factory\nfunction with signature ``name, bases, dic``:\n\n.. code:: python\n\n >>> print include(Pack, Place, Grid) \n \n\nThis function will create the class by using a suitable\nmetaclass:\n\n.. code:: python\n\n >>> type(TOSWidget)\n \n\nIn simple cases the metaclass will be ``MetaTOS``, the main class\nof the trait object system, but in general it can be a different\none not inheriting from ``MetaTOS``. The exact rules followed by\n``include`` to determine the right class will be discussed later.\n\nHere I want to remark that according to rule 6 traits take the precedence\nover the base class attributes. Consider the following example:\n\n.. code:: python\n\n >>> class Base(object):\n ... a = 1\n\n >>> class ATrait(object):\n ... a = 2\n\n >>> class Class(Base):\n ... __metaclass__ = include(ATrait)\n\n >>> Class.a\n 2\n\nIn regular multiple inheritance you would do the same by including\n``ATrait`` *before* ``Base``, i.e.\n\n.. code:: python\n\n >>> type('Class', (ATrait, Base), {}).a\n 2\n\nYou should take care to not mix-up the order, otherwise you will get a\ndifferent result:\n\n.. code:: python\n\n >>> type('Class', (Base, ATrait), {}).a\n 1\n\nTherefore replacing mixin classes with traits can break your code if\nyou rely on the order. Be careful!\n\nThe Trait Object System\n----------------------------------------------------------------------\n\nThe goal of the ``strait`` module it to modify the standard\nPython object model, turning it into a Trait Object System (TOS for short): \nTOS classes behave differently from regular\nclasses. In particular TOS classes do not support multiple inheritance.\nIf you try to multiple inherit from a TOS\nclass and another class you will get a ``TypeError``:\n\n.. code:: python\n\n >>> class M:\n ... \"An empty class\"\n ...\n >>> class Widget2(TOSWidget, M): \n ... pass\n ...\n Traceback (most recent call last):\n ...\n TypeError: Multiple inheritance of bases (, ) is forbidden for TOS classes\n\nThis behavior is intentional: with this restriction you can simulate\nan ideal world in which Python did not support multiple\ninheritance. Suppose you want to claim that supporting multiple\ninheritance was a mistake and that Python would have been better off\nwithout it (which is the position I tend to have nowadays): how can\nyou prove that claim? Simply by writing code that does not use\nmultiple inheritance and it is clearer and more mantainable that code\nusing multiple inheritance.\n\nI am releasing this trait implementation hoping you will help me to\nprove (or possibly disprove) the point. You may see traits as a\nrestricted form of multiple inheritance without name clashes,\nwithout the complications of the method resolution, and with a\nlimited cooperation between methods.\nMoreover the present implementation is slightly less dynamic\nthan usual inheritance.\n\nA nice property of inheritance is that if you have a class ``C`` inheriting\nfrom class ``M`` and you change a method in ``M`` at runtime, after\n``C`` has been created and instantiated, automagically all instances\nof ``C`` gets the new version of the method, which is pretty useful\nfor debugging purposes. This feature is lost in the trait implementation\nprovided here. Actually, in a previous version, my trait implementation\nwas fully dynamic and if you changed the mixin the instances would be\nchanged too. However, I never used that feature in practice, and\nit was complicating the implementation and slowing doing the\nattribute access, so I removed it.\n\nI think these are acceptable restrictions since they give back\nin return many advantages in terms of simplicity: for instance,\n``super`` becomes trivial, since each class has a single superclass,\nwhereas we all know that the `current super in Python`_ is very far\nfrom trivial.\n\n.. _current super in Python: http://www.artima.com/weblogs/viewpost.jsp?thread=236275\n.. _elsewhere: http://www.artima.com/weblogs/viewpost.jsp?thread=246341\n.. _PloneSite hierarchy: http://www.phyast.pitt.edu/~micheles/python/plone-hierarchy.png \n\nThe magic of ``include``\n------------------------------------------------------\n\nSince the fundamental properties of TOS classes must be preserved under \ninheritance (i.e. the son of a TOS class must be a TOS class) \nthe implementation necessarily requires metaclasses. As of now,\nthe only fundamental property of a TOS class is that multiple\ninheritance is forbidden, so usually (*but not always*) TOS\nclasses are instances of the metaclass ``MetaTOS``\nwhich implements a single inheritance check.\nIf you build your TOS hierarchy starting from pre-existing classes,\nyou should be aware of how ``include`` determines the metaclass: \nif your base class was an old-style\nclass or a plain new style class (i.e. a direct instance of the\n``type`` metaclass), them ``include`` will change it to ``MetaTOS``:\n\n.. code:: python\n\n >>> type(TOSWidget)\n \n\nIn general you may need to build your Trait Based Framework\non top of pre-existing classes possessing a nontrivial metaclass, for\ninstance Zope classes; in that case ``include`` is smart\nenough to figure out the right metaclass to use. Here is an example:\n\n.. code:: python\n\n class AddGreetings(type):\n \"A metaclass adding a 'greetings' attribute for exemplification purposes\"\n def __new__(mcl, name, bases, dic):\n dic['greetings'] = 'hello!'\n return super(AddGreetings, mcl).__new__(mcl, name, bases, dic)\n\n\n.. code:: python\n\n class WidgetWithGreetings(BaseWidget, object):\n __metaclass__ = AddGreetings\n\n\n.. code:: python\n\n class PackWidget(WidgetWithGreetings):\n __metaclass__ = include(Pack)\n\n\n``include`` automatically generates the right metaclass as\na subclass of ``AddGreetings``:\n\n.. code:: python\n\n >>> print type(PackWidget).__mro__\n (, , , )\n \nIncidentally, since TOS\nclasses are guaranteed to be in a straight hierarchy, ``include`` is able\nto neatly avoid the dreaded `metaclass conflict`_.\n\nThe important point is that ``_TOSAddGreetings`` provides the same features of\n``MetaTOS``, even if it is not a subclass of it; on the\nother hand, ``_TOSMetaAddGreetings`` is a subclass of ``AddGreetings``\nwhich calls ``AddGreetings.__new__``, so the features provided by\n``AddGreetings`` are not lost either; in this example you may check\nthat the greetings attribute is correctly set:\n\n.. code:: python\n\n >>> PackWidget.greetings\n 'hello!'\n\nThe name of the generated metaclass\nis automatically generated from the name of the base\nmetaclass; moreover, a register of the generated metaclasses\nis kept, so that metaclasses are reused if possible.\nIf you want to understand the details, you are welcome\nto give a look at the implementation, which is pretty short\nand simple, compared to the general recipe to remove\nthe metaclass conflict in a true multiple inheritance situation.\n\n.. _sqlalchemy: http://www.sqlalchemy.org/\n.. _metaclass conflict: http://code.activestate.com/recipes/204197/\n\nCooperative traits\n------------------------------------------------------------\n\nAt first sight, the Trait Object System lacks an important feature of\nmultiple inheritance as implemented in the ordinary Python object system,\ni.e. cooperative methods. Consider for instance the following\nclasses:\n\n.. code:: python\n\n class LogOnInitMI(object):\n def __init__(self, *args, **kw):\n print 'Initializing %s' % self\n super(LogOnInitMI, self).__init__(*args, **kw)\n\n.. code:: python\n\n class RegisterOnInitMI(object):\n register = []\n def __init__(self, *args, **kw):\n print 'Registering %s' % self\n self.register.append(self)\n super(RegisterOnInitMI, self).__init__(*args, **kw)\n\n\nIn multiple inheritance ``LogOnInitMI`` can be mixed with other\nclasses, giving to the children the ability to log on initialization;\nthe same is true for ``RegisterOnInitMI``, which gives to its children\nthe ability to populate a registry of instances. The important feature\nof the multiple inheritance system is that ``LogOnInitMI`` and\n``RegisterOnInitMI`` play well together: if you inherits from\nboth of them, you get both features:\n\n.. code:: python\n\n class C_MI(LogOnInitMI, RegisterOnInitMI):\n pass\n\n\n.. code:: python\n\n >>> c = C_MI() \n Initializing <__main__.C_MI object at 0x...>\n Registering <__main__.C_MI object at 0x...>\n\nYou cannot get the same behaviour if you use the trait object system\nnaively:\n\n.. code:: python\n\n >>> class C_MI(object):\n ... __metaclass__ = include(LogOnInitMI, RegisterOnInitMI)\n ...\n Traceback (most recent call last):\n ...\n OverridingError: LogOnInitMI overrides names in RegisterOnInitMI: {__init__}\n\nThis is a feature, of course, since the trait object system is designed\nto avoid name clashes. However, the situation is worse than that:\neven if you try to mixin a single class you will run into trouble\n\n.. code:: python\n\n >>> class C_MI(object):\n ... __metaclass__ = include(LogOnInitMI)\n >>> c = C_MI()\n Traceback (most recent call last):\n ...\n TypeError: super(type, obj): obj must be an instance or subtype of type\n\nWhat's happening here? The situation is clear if you notice that the\n``super`` call is actually a call of kind ``super(LogOnInitMI, c)``\nwhere ``c`` is an instance of ``C``, which is not a\nsubclass of ``LogOnInitMI``. That explains the\nerror message, but does not explain how to solve the issue. It seems\nthat method cooperation using ``super`` is impossible for TOS\nclasses. \n\nActually this is not the case: single inheritance cooperation\nis possible and it is enough as we will show in a\nminute. But for the moment let me notice that I do not think\nthat cooperative methods are necessarily a good idea. They are\nfragile and cause all of your classes to be strictly coupled. My usual\nadvice if that you should not use a design based on method\ncooperation if you can avoid it.\nHaving said that, there are situations (very rare) where you\nreally want method cooperation. The ``strait`` module provide\nsupport for those situations via the ``__super`` attribute.\n\nLet me explain how it works. When you mix-in a trait ``T`` into a\nclass ``C``, ``include`` adds an attribute ``_T__super`` to ``C``,\nwhich is a ``super`` object that dispatches to the attributes of the\nsuperclass of ``C``. The important thing to keep in mind is that there\nis a well defined superclass, since the trait object system uses\nsingle inheritance only. Since the hierarchy is straight, the\ncooperation mechanism is much simpler to understand than in multiple\ninheritance. Here is an example. First of all, let me rewrite\n``LogOnInit`` and ``RegisterOnInit`` to use ``__super`` instead of\n``super``:\n\n.. code:: python\n\n class LogOnInit(object):\n def __init__(self, *args, **kw):\n print 'Initializing %s' % self\n self.__super.__init__(*args, **kw)\n\n.. code:: python\n\n class RegisterOnInit(object):\n register = []\n \n def __init__(self, *args, **kw):\n print 'Registering %s' % self\n self.register.append(self)\n self.__super.__init__(*args, **kw)\n\n\nNow you can include the ``RegisterOnInit`` functionality as follows:\n\n.. code:: python\n\n class C_Register(object):\n __metaclass__ = include(RegisterOnInit)\n\n\n.. code:: python\n\n >>> _ = C_Register() \n Registering <__main__.C_Register object at 0x...>\n\nEverything works because ``include`` has added the right attribute:\n\n.. code:: python\n\n >>> C_Register._RegisterOnInit__super\n , >\n\nMoreover, you can also include the ``LogOnInit`` functionality:\n\n.. code:: python\n\n class C_LogAndRegister(C_Register):\n __metaclass__ = include(LogOnInit)\n\n\n.. code:: python\n\n >>> _ = C_LogAndRegister() \n Initializing <__main__.C_LogAndRegister object at 0x...>\n Registering <__main__.C_LogAndRegister object at 0x...>\n\nAs you see, the cooperation mechanism works just fine. I will call\n*cooperative trait* a class intended for inclusion in other classes\nand making use of the ``__super`` trick. A class using the \nregular ``super`` directly cannot be used as a cooperative trait, since it\nmust satisfy inheritance constraints, nevertherless it is easy enough to\nconvert it to use ``__super``. After all, the ``strait`` module is\nintended for framework writers, so it assumes you can change the\nsource code of your framework if you want. On the other hand, if\nare trying to re-use a mixin class coming from a third party\nframework and using ``super``, you will have to rewrite the\nparts of it. That is unfortunate, but I cannot perform miracles.\n\nYou may see ``__super`` as a clever hack to use\n``super`` indirectly. Notice that since the hierarchy is straight, \nthere is room for optimization at the core language\nlevel. The ``__super`` trick as implemented in pure Python leverages\non the name mangling mechanism, and follows closely the famous\n`autosuper recipe`_, with some improvement. Anyway,\nif you have two traits with the same\nname, you will run into trouble. To solve this and to have a nicer\nsyntax, one would need more support from the language, but the\n``__super`` trick is good enough for a prototype and\nhas the serious advantage of working right now for current Python.\n\n.. _autosuper recipe: http://www.python.org/download/releases/2.2.3/descrintro/#metaclass_examples\n\nCooperation at the metaclass level\n---------------------------------------------------------------\n\nIn my experience, the cases where you need method cooperation\nin multiple inheritance situations are exceedingly rare,\nunless you are a language implementor or a designer of\nvery advanced frameworks. In such a realm you have a need for\ncooperative methods; it is not a pressing need, in the sense that\nyou can always live without them, but they are a nice feature to have if you\ncare about elegance and extensibility. For instance, as P. J. Eby\npoints it out in this `thread on python-dev`_:\n\n*A major use case for co-operative super() is in the implementation of\nmetaclasses. The __init__ and __new__ signatures are fixed, multiple\ninheritance is possible, and co-operativeness is a must (as the base\nclass methods* must *be called). I'm hard-pressed to think of a\nmetaclass constructor or initializer that I've written in the last\nhalf-decade or more where I didn't use super() to make it\nco-operative. That, IMO, is a compelling use case even if there were\nnot a single other example of the need for super.*\n\nI have always felt the same. So, even if I have been unhappy with multiple\ninheritance for years, I could never dismiss it entirely\nbecause of the concern for this use case. It is only after discovering\ncooperative traits that I felt the approach powerful enough\nto replace multiple inheritance without losing anything I cared about.\n\nMultiple inheritance at the metaclass level comes out here and\nagain when you are wearing the language implementor hat. For instance,\nif you try to implement an object system based on traits, you will have to do\nso at the metaclass level and there method cooperation has its place.\nIn particular, if you look at the source code of the ``strait`` module -\nwhich is around 100 lines, a tribute to the power of Python -\nyou will see that the ``MetaTOS`` metaclass is implemented\nas a cooperative trait, so that it can be mixed-in with other metaclasses,\nin the case you are interoperating with a framework with a non-trivial\nmeta object protocol. This is performed internally by ``include``.\n\nMetaclass cooperation is there to make the life of the users\neasier. Suppose one of you, users of the ``strait`` module, wants to\nenhance the ``include`` mechanism using another a metaclass coming for\na third party framework and therefore not inheriting from ``MetaTOS``:\n\n.. code:: python\n\n class ThirdPartyMeta(type):\n def __new__(mcl, name, bases, dic):\n print 'Using ThirdPartyMeta to create %s' % name\n return super(ThirdPartyMeta, mcl).__new__(mcl, name, bases, dic)\n\n\nThe way to go is simple. First, you should mix-in ``MetaTOS`` in the \nthird party class:\n\n.. code:: python\n\n class EnhancedMetaTOS(ThirdPartyMeta):\n __metaclass__ = include(MetaTOS)\n\n\nThen, you can define your own enhanced ``include`` as follows:\n\n.. code:: python\n\n def enhanced_include(*traits):\n return include(MetaTOS=EnhancedMetaTOS, *traits)\n\n\nIn simple cases using directly ``ThirdPartyMeta`` may work, but I strongly\nrecommend to replace the call to super with ``__super`` even in \n``ThirdPartyMeta`` to make the cooperation robust.\n\n.. _thread on python-dev: http://www.gossamer-threads.com/lists/python/dev/673833\n\nDiscussion of some design decisions and future work\n--------------------------------------------------------\n\nThe decision of having TOS classes which are not instances of \n``MetaTOS``\nrequired some thought. That was my original idea in version 0.1 of\n``strait``; however in version 0.2 I wanted to see what would happen\nif I made all TOS classes instances of ``MetaTOS``. \nThat implied that if\nyour original class had a nontrivial metaclass, then the TOS class had\nto inherit both from the original metaclass *and* ``MetaTOS``,\ni.e. multiple inheritance and cooperation of methods was required at\nthe metaclass level. \n\nI did not like it, since I was arguing that\nyou can do everything without multiple inheritance; moreover using \nmultiple inheritance at the metaclass level\nmeant that one had to solve the metaclass conflict in a general\nway. I did so, by using my own cookbook recipe, and all my tests\npassed.\n\nNeverthess, at the end, in version 0.3 I decided to go back to the\noriginal design. The metaclass conflict recipe is too complex, and I\nsee it as a code smell - *if the implementation is hard to explain,\nit's a bad idea* - just another indication that multiple inheritance\nis bad. In the original design it is possible to add the features of\n``MetaTOS`` to the original metaclass by subclassing it with *single*\ninheritance and thus avoiding the conflict.\n\nThe price to pay is that now a TOS class is no more an instance of\n``MetaTOS``, but this is a non-issue: the important\nthing is that TOS classes perform the dispatch on their traits as\n``MetaTOS`` would dictate. Moreover, starting from\nPython 2.6, thanks to `Abstract Base Classes`_, you may satisfy the\n``isinstance(obj, cls)`` check even if ``obj`` is not an instance of\n``cls``, by registering a suitable base class (similarly for\n``issubclass``). In our situation, that means that it is enough to\nregister ``MetaTOS`` as base class of the original\nmetaclass.\n\nVersion 0.4 was much more complex that the current version (still\nshort, it was under 300 lines of pure Python), since it had the more\nambitious goal of solving the namespace pollution problem. I have\ndiscussed the issue elsewhere_: if you keep injecting methods into a\nclass (both directly or via inheritance) you may end up having\nhundreds of methods flattened at the same level. \n\nA picture is worth a\nthousand words, so have a look at the `PloneSite hierarchy`_ if you\nwant to understand the horror I wanted to avoid with traits (the\npicture shows the number of nonspecial attributes defined per class in\nsquare brackets): in the Plone Site hierarchy there are 38 classes, 88\noverridden names, 42 special names, 648 non-special attributes and\nmethods. It is a nighmare. \n\nOriginally I wanted to prevent this kind\nof abuse, but that made my implementation more complex, whereas\nmy main goal was to keep the implementation simple. As a consequence\nthis version assumes the prosaic attitude that you cannot stop\nprogrammers from bad design anyway, so if they want to go the Zope\nway they can.\n\nIn previous versions I did provide some syntactic sugar for ``include``\nso that it was possible to write something like the following\n(using a trick discussed here_):\n\n.. code:: python\n\n class C(Base):\n include(Trait1, Trait2)\n\nIn version 0.5 I decided to remove this feature. Now the plumbing\n(i.e. the ``__metaclass__`` hook) is exposed to the user, some magic\nhas been removed and it is easier for the user to write her own\n``include`` factory if she wants to.\n\nWhere to go from here? For the moment, I have no clear idea about the\nfuture. The Smalltalk implementation of traits provides method\nrenaming out of the box. The Python implementation has no facilities\nin this sense. In the future I may decide to give some support for\nrenaming, or I may not. At the present you can just rename your\nmethods by hand. Also, in the future I may decide to add some kind of\nadaptation mechanism or I may not: after all the primary goal of this\nimplementation is semplicity and I don't want to clutter it with too\nmany features.\n\nI am very open to feedback and criticism: I am releasing this module\nwith the hope that it will be used in real life situations to gather\nexperience with the traits concept. Clearly I am not proposing that\nPython should remove multiple inheritance in favor of traits:\nconsiderations of backward compatibily would kill the proposal right\nfrom the start. I am just looking for a few adventurous volunteers\nwanting to experiment with traits; if the experiment goes well, and\npeople start using (multiple) inheritance less than they do now, I\nwill be happy.\n\nTrivia\n--------------------------------\n\n``strait`` officially stands for Simple Trait object system, however\nthe name is also a pun on the world \"straight\", since the difference\nbetween multiple inheritance hierarchies and TOS hierarchies is that\nTOS hierarchies are straight. Moreover, nobody will stop you from\nthinking that the ``s`` also stands for Simionato ;)\n\n.. _Abstract Base Classes: http://www.python.org/dev/peps/pep-3119/\n.. _traits: http://www.iam.unibe.ch/~scg/Research/Traits/\n.. _Traits - Composable Units of Behavior: http://www.iam.unibe.ch/%7Escg/Archive/Papers/Scha03aTraits.pdf\n.. _PLT Scheme: http://www.cs.utah.edu/plt/publications/aplas06-fff.pdf\n.. _here: http://www.ibm.com/developerworks/linux/library/l-pymeta3.html", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://pypi.python.org/pypi/strait", "keywords": "", "license": "BSD License", "maintainer": null, "maintainer_email": null, "name": "strait", "package_url": "https://pypi.org/project/strait/", "platform": "any", "project_url": "https://pypi.org/project/strait/", "project_urls": { "Download": "UNKNOWN", "Homepage": "http://pypi.python.org/pypi/strait" }, "release_url": "https://pypi.org/project/strait/0.5.3/", "requires_dist": null, "requires_python": null, "summary": "Simple Traits for Python", "version": "0.5.3" }, "last_serial": 1679547, "releases": { "0.5.0": [ { "comment_text": "", "digests": { "md5": "1d9f8c41b59ae3f229139f74be20903c", "sha256": "750e91112bfb6dd5d2db5729f83293e4f1f43254656801512f130c2d5f47f590" }, "downloads": -1, "filename": "strait-0.5.0.tar.gz", "has_sig": false, "md5_digest": "1d9f8c41b59ae3f229139f74be20903c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15740, "upload_time": "2009-01-04T12:17:11", "url": "https://files.pythonhosted.org/packages/14/85/a1a3537e8f249b47609d1623d33aa6af86c429136b9ff1222406281eda0b/strait-0.5.0.tar.gz" } ], "0.5.1": [ { "comment_text": "", "digests": { "md5": "81ba4319dc5144dbb3a47d70222df63d", "sha256": "c7ff13d01453481bfd9bd7dbbb2211c0d8b4aa00213175e0fd0df2f423588210" }, "downloads": -1, "filename": "strait-0.5.1.tar.gz", "has_sig": false, "md5_digest": "81ba4319dc5144dbb3a47d70222df63d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 104027, "upload_time": "2009-01-05T17:04:11", "url": "https://files.pythonhosted.org/packages/bd/02/18e3a17d692232b13750d7305d81366fa6ffce9616e08a3480d7eb61bfcc/strait-0.5.1.tar.gz" } ], "0.5.2": [ { "comment_text": "", "digests": { "md5": "5ad1266891ac134a2b9e5ade7dd5ed86", "sha256": "e7029d0fcdf977e47ce6b5b8cfa601e4b69e123a8c84e579b59ba79f4e8d13af" }, "downloads": -1, "filename": "strait-0.5.2.tar.gz", "has_sig": false, "md5_digest": "5ad1266891ac134a2b9e5ade7dd5ed86", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 25527, "upload_time": "2015-08-16T16:24:11", "url": "https://files.pythonhosted.org/packages/85/ec/e31b1177011bb2798af6739a4c85702af96ca027f9f0bd630bd243322e03/strait-0.5.2.tar.gz" } ], "0.5.3": [ { "comment_text": "", "digests": { "md5": "051a2446f0776a900d8675f0a61801bb", "sha256": "8599dec6db3c2fda5ab2bb9ba844ae00ed2f3222e3f94f8140329c82f0eadf42" }, "downloads": -1, "filename": "strait-0.5.3.tar.gz", "has_sig": false, "md5_digest": "051a2446f0776a900d8675f0a61801bb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 58594, "upload_time": "2015-08-16T17:05:14", "url": "https://files.pythonhosted.org/packages/57/34/232f0507738c5415b2d3add0ea396a534e7846410331096b92e50968f96f/strait-0.5.3.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "051a2446f0776a900d8675f0a61801bb", "sha256": "8599dec6db3c2fda5ab2bb9ba844ae00ed2f3222e3f94f8140329c82f0eadf42" }, "downloads": -1, "filename": "strait-0.5.3.tar.gz", "has_sig": false, "md5_digest": "051a2446f0776a900d8675f0a61801bb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 58594, "upload_time": "2015-08-16T17:05:14", "url": "https://files.pythonhosted.org/packages/57/34/232f0507738c5415b2d3add0ea396a534e7846410331096b92e50968f96f/strait-0.5.3.tar.gz" } ] }