summaryrefslogtreecommitdiff
path: root/pypers/meta.txt
diff options
context:
space:
mode:
Diffstat (limited to 'pypers/meta.txt')
-rwxr-xr-xpypers/meta.txt1024
1 files changed, 1024 insertions, 0 deletions
diff --git a/pypers/meta.txt b/pypers/meta.txt
new file mode 100755
index 0000000..31adcf4
--- /dev/null
+++ b/pypers/meta.txt
@@ -0,0 +1,1024 @@
+THE MAGIC OF METACLASSES - PART I
+==========================================================================
+
+ .. line-block::
+
+ *Metaclasses are deeper magic than 99% of users should ever
+ worry about. If you wonder whether you need them, you don't
+ (the people who actually need them know with certainty that
+ they need them, and don't need an explanation about why).*
+ --Tim Peters
+
+Python always had metaclasses, since they are inherent to its object
+model. However, before Python 2.2, metaclasses where tricky and their
+study could cause the programmer's brain to explode [#]_. Nowadays,
+the situation has changed, and the reader should be able to understand
+this chapter without risk for his/her brain (however I do not give any
+warranty ;)
+
+Put it shortly, metaclasses give to the Python programmer
+complete control on the creation of classes. This simple statement
+has far reaching consequences, since the ability of interfering with
+the process of class creation, enable the programmer to make miracles.
+
+In this and in the following chapters, I will show some of these
+miracles.
+
+This chapter will focus on subtle problems of metaclasses in inheritance
+and multiple inheritance, including multiple inheritance of metaclasses
+with classes and metaclasses with metaclasses.
+
+The next chapter will focus more on applications.
+
+
+.. [#] Metaclasses in Python 1.5 [A.k.a the killer joke]
+ http://www.python.org/doc/essays/metaclasses/
+
+There is very little documentation about metaclasses, except Guido's
+essays and the papers by David Mertz and myself published in IBMdeveloperWorks
+
+ http://www-106.ibm.com/developerworks/library/l-pymeta.html
+
+Metaclasses as class factories
+------------------------------------------------------------------------
+
+In the Python object model (inspired from the Smalltalk, that had metaclasses
+a quarter of century ago!) classes themselves are objects.
+Now, since objects are instances of classes, that means that classes
+themselves can be seen as instances of special classes called *metaclasses*.
+Notice that things get hairy soon, since by following this idea, one could
+say the metaclasses themselves are classes and therefore objects; that
+would mean than even metaclasses can be seen as
+instances of special classes called meta-metaclasses. On the other hand,
+meta-meta-classes can be seen as instances of meta-meta-metaclasses,
+etc. Now, it should be obvious why metaclasses have gained such a
+reputation of brain-exploders ;). However, fortunately, the situation
+is not so bad in practice, since the infinite recursion of metaclasses is
+avoided because there is a metaclass that is the "mother of all metaclasses":
+the built-in metaclass *type*. 'type' has the property of being its own
+metaclass, therefore the recursion stops. Consider for instance the following
+example:
+
+ >>> class C(object): pass # a generic class
+ >>> type(C) #gives the metaclass of C
+ <type 'type'>
+ >>> type(type(C)) #gives the metaclass of type
+ <type 'type'>
+
+The recursion stops, since the metaclass of 'type' is 'type'.
+One cool consequence of classes being instances of 'type',
+is that since *type* is a subclass of object,
+
+ >>> issubclass(type,object)
+ True
+
+any Python class is not only a subclass of ``object``, but also
+an instance of 'object':
+
+ >>> isinstance(C,type)
+ True
+ >>> isinstance(C,object)
+ True
+ >>> issubclass(C,object)
+ True
+
+Notice that 'type' is an instance of itself (!) and therefore of 'object':
+
+ >>> isinstance(type,type) # 'type' is an instance of 'type'
+ True
+ >>> isinstance(type,object) # therefore 'type' is an instance of 'object'
+ True
+
+As it is well known, ``type(X)`` returns the type of ``X``; however,
+``type`` has also a second form in which it acts as a class factory.
+The form is ``type(name,bases,dic)`` where ``name`` is the name of
+the new class to be created, bases is the tuple of its bases and dic
+is the class dictionary. Let me give a few examples:
+
+ >>> C=type('C',(),{})
+ >>> C
+ <class '__main__.C'>
+ >>> C.__name__
+ 'C'
+ >>> C.__bases__
+ (<type 'object'>,)
+ >>> C.__dict__
+ <dict-proxy object at 0x8109054>
+
+Notice that since all metaclasses inherits from ``type``, as a consequences
+all metaclasses can be used as class factories.
+
+A fairy tale example will help in understanding the concept
+and few subtle points on how attributes are transmitted from metaclasses
+to their instances.
+
+Let me start by defining a 'Nobility' metaclass :
+
+ >>> class Nobility(type): attributes="Power,Richness,Beauty"
+
+instances of 'Nobility' are classes such 'Princes', 'Dukes', 'Barons', etc.
+
+ >>> Prince=Nobility("Prince",(),{})
+
+Instances of 'Nobility' inherits its attributes, just as instances of normal
+classes inherits the class docstring:
+
+ >>> Prince.attributes
+ 'Power,Richness,Beauty'
+
+Nevertheless, 'attributes' will not be retrieved by the ``dir`` function:
+
+ >>> print dir(Prince)
+ ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__',
+ '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__repr__',
+ '__setattr__', '__str__', '__weakref__']
+
+However, this is a limitation of ``dir``, in reality ``Prince.attributes``
+is there. On the other hand, the situation is different for a specific
+'Prince' object
+
+ >>> charles=Prince()
+ >>> charles.attributes #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ AttributeError: 'Prince' object has no attribute 'attributes'
+
+The transmission of metaclass attributes is not transitive:
+instances of the metaclass inherits the attributes, but not the instances
+of the instances. This behavior is by design and is needed in order to avoid
+troubles with special methods. This point will be throughly
+explained in the last paragraph. For the moment, I my notice that the
+behaviour is reasonable, since the abstract qualities 'Power,Richness,Beauty'
+are more qualities of the 'Prince' class than of one specific representative.
+They can always be retrieved via the ``__class__`` attribute:
+
+ >>> charles.__class__.attributes
+ 'Power,Richness,Beauty'
+
+Le me now define a metaclass 'Froggyness':
+
+ >>> class Frogginess(type): attributes="Powerlessness,Poverty,Uglyness"
+
+Instances of 'Frogginess' are classes like 'Frog', 'Toad', etc.
+
+ >>> Frog=Frogginess("Frog",(),{})
+ >>> Frog.attributes
+ 'Powerlessness,Poverty,Uglyness'
+
+However, in Python miracles can happen:
+
+ >>> def miracle(Frog): Frog.__class__=Nobility
+ >>> miracle(Frog); Frog.attributes
+ 'Powerlessness,Richness,Beauty'
+
+In this example a miracle happened on the class 'Frog', by changing its
+(meta)class to 'Nobility'; therefore its attributes have changed accordingly.
+
+However, there is subtle point here. Suppose we explicitly specify the 'Frog'
+attributes, in such a way that it can be inherited by one of its specific
+representative:
+
+ >>> Frog.attributes="poor, small, ugly"
+ >>> jack=Frog(); jack.attributes
+ 'poor, small, ugly'
+
+Then the miracle cannot work:
+
+ ::
+
+ #<fairytale2.py>
+
+ class Nobility(type): attributes="Power, Richness, Beauty"
+ Prince=Nobility("Prince",(),{})
+ charles=Prince()
+
+ class Frogginess(type): attributes="Inpuissance, Poverty, Uglyness"
+ Frog=Frogginess("Frog",(),{})
+ Frog.attributes="poor, small, ugly"
+ jack=Frog()
+
+ def miracle(Frog): Frog.__class__=Nobility
+
+ miracle(Frog)
+
+ print "I am",Frog.attributes,"even if my class is",Frog.__class__
+
+ #</fairytale2.py>
+
+Output:
+
+ ::
+
+ I am poor, small, ugly even if my class is <class '__main__.Nobility'>
+
+The reason is that Python first looks at specific attributes of an object
+(in this case the object is the class 'Frog') an only if they are not found,
+it looks at the attributes of its class (here the metaclass 'Nobility').Since
+in this example the 'Frog' class has explicit attributes, the
+result is ``poor, small, ugly``. If you think a bit, it makes sense.
+
+Remark:
+
+In Python 2.3 there are restrictions when changing the ``__class__``
+attribute for classes:
+
+ >>> C=type('C',(),{})
+ >>> C.__class__ = Nobility #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: __class__ assignment: only for heap types
+
+Here changing ``C.__class__`` is not allowed, since 'C' is an instance
+of the built-in metaclass 'type'. This restriction, i.e. the fact that
+the built-in metaclass cannot be changed, has been imposed for
+security reasons, in order to avoid dirty tricks with the built-in
+classes. For instance, if it was possible to change the metaclass
+of the 'bool' class, we could arbitrarily change the behavior of
+boolean objects. This could led to abuses.
+Thanks to this restriction,
+the programmer is always sure that built-in classes behaves as documented.
+This is also the reason why 'bool' cannot be subclassed:
+
+ >>> print bool.__doc__ # in Python 2.2 would give an error
+ bool(x) -> bool
+ Returns True when the argument x is true, False otherwise.
+ The builtins True and False are the only two instances of the class bool.
+ The class bool is a subclass of the class int, and cannot be subclassed.
+
+In any case, changing the class of a class is not a good idea, since it
+does not play well with inheritance, i.e. changing the metaclass of a base
+class does not change the metaclass of its children:
+
+ >>> class M1(type): f=lambda cls: 'M1.f' #metaclass1
+ >>> class M2(type): f=lambda cls: 'M2.f' #metaclass2
+ >>> B=M1('B',(),{}) # B receives M1.f
+ >>> class C(B): pass #C receives M1.f
+ >>> B.f()
+ 'M1.f'
+ B.__class__=M2 #change the metaclass
+ >>> B.f() #B receives M2.f
+ 'M2.f'
+ C.f() #however C does *not* receive M2.f
+ >>> C.f()
+ 'M1.f'
+ >>> type(B)
+ <class '__main__.M2'>
+ >>> type(C)
+ <class '__main__.M1'>
+
+Metaclasses as class modifiers
+----------------------------------------------------------------------
+
+The interpretation of metaclasses in terms of class factories is quite
+straightforward and I am sure that any Pythonista will be at home
+with the concept. However, metaclasses have such a reputation of black
+magic since their typical usage is *not* as class factories, but as
+*class modifiers*. This means that metaclasses are typically
+used to modify *in fieri* classes. The trouble is that the
+modification can be utterly magical.
+Here there is another fairy tale example showing the syntax
+(via the ``__metaclass__`` hook) and the magic of the game:
+
+ ::
+
+ #<oopp.py>
+
+ class UglyDuckling(PrettyPrinted):
+ "A plain, regular class"
+ formatstring="Not beautiful, I am %s"
+
+ class MagicallyTransformed(type):
+ "Metaclass changing the formatstring of its instances"
+ def __init__(cls,*args):
+ cls.formatstring="Very beautiful, since I am %s"
+
+ class TransformedUglyDuckling(PrettyPrinted):
+ "A class metamagically modified"
+ __metaclass__ = MagicallyTransformed
+ formatstring="Not beautiful, I am %s" # will be changed
+
+ #</oopp.py>
+
+ >>> from oopp import *
+ >>> print UglyDuckling()
+ Not beautiful, I am <UglyDuckling>
+
+In this example, even if in 'TransformedUglyDuckling' we explicitely
+set the formatstring to "Not beautiful, I am %s", the metaclass changes
+it to "Very beautiful, even if I am %s" and thus
+
+ >>> print TransformedUglyDuckling() # gives
+ Very beautiful, since I am <TransformedUglyDuckling>
+
+Notice that the ``__metaclass__`` hook passes to the metaclass
+``MagicallyTransformed`` the name, bases and dictionary of the class
+being created, i.e. 'TransformedUglyDucking'.
+
+Metaclasses, when used as class modifiers, act *differently*
+from functions, when inheritance is
+involved. To clarify this subtle point, consider a subclass 'Swan'
+of 'UglyDuckling':
+
+
+ >>> from oopp import *
+ >>> class Swan(UglyDuckling):
+ ... formatstring="Very beautiful, I am %s"
+ >>> print Swan()
+ Very beautiful, I am <Swan>
+
+Now, let me define a simple function acting as a class modifier:
+
+ >>> def magicallyTransform(cls):
+ ... "Modifies the class formatstring"
+ ... customize(cls,formatstring="Very beautiful, even if I am %s")
+ ... return cls
+
+The function works:
+
+ >>> magicallyTransform(UglyDuckling)
+ >>> print UglyDuckling()
+ Very beautiful, even if I am <UglyDuckling>
+
+This approach is destructive, since we cannot have the original
+and the transformed class at the same time, and has potentially bad side
+effects in the derived classes. Nevertheless, in this case it works
+and it is not dangereous for the derived class 'Swan', since 'Swan'
+explicitly overrides the 'formatstring' attribute and doesn't care about
+the change in 'UglyDuckling.formatstring'. Therefore the output
+of
+
+ >>> print Swan()
+ Very beautiful, I am <Swan>
+
+is still the same as before the action of the function ``magicallyTransform``.
+The situation is quite different if we use the 'MagicallyTransformed'
+metaclass:
+
+ >>> from oopp import *
+ >>> class Swan(TransformedUglyDuckling):
+ ... formatstring="Very beautiful, I am %s"
+
+ >>> print TransformedUglyDuckling()
+ Very beautiful, since I am <UglyDuckling>
+ >>> print Swan() # does *not* print "Very beautiful, I am <Swan>"
+ Very beautiful, since I am <Swan>
+
+Therefore, not only the metaclass has magically transformed the
+'TransformedUglyDuckling.formatstring', it has also transformed the
+'Swan.formatstring'! And that, despite the fact that
+'Swan.formatstring' is explicitly set.
+
+The reason for this behaviour is that since 'UglyDuckling' is a base
+class with metaclass 'MagicallyTransformed', and since 'Swan' inherits from
+'UglyDuckling', then 'Swan' inherits the metaclass 'MagicallyTransformed',
+which is automatically called at 'Swan' creation time.
+That's the reason why metaclasses are much more magical and much
+more dangerous than
+functions: functions do not override attributes in the derived classes,
+metaclasses do, since they are automagically called at the time of
+creation of the subclass. In other words, functions are explicit,
+metaclasses are implicit. Nevertheless, this behavior can be pretty
+useful in many circumstances, and it is a feature, not a bug. In the
+situations where this behavior is not intended, one should use a function,
+not a metaclass. In general, metaclasses are better than functions,
+since metaclasses are classes and as such they can inherit one from each
+other. This means that one can improve a basic metaclass trough
+(multiple) inheritance, with *reuse* of code.
+
+A few caveats about the usage of metaclasses
+------------------------------------------------------------------------
+
+Let me start with some caveats about the ``__metaclass__`` hook, which
+commonly used and quite powerful, but also quite dangereous.
+
+Let's imagine a programmer not
+knowing about metaclasses and looking at the 'TransformedUglyDuckling'
+code (assuming there are no comments): she would probably think
+that "__metaclass__" is some special attribute used for introspection
+purposes only, with no other effects, and she would probably expect
+the output of the script to be "Not much, I am the class
+TransformedUglyDucking" whereas it is exacly the contrary! In other
+words, when metaclasses are involved, *what you see, is not what you get*.
+The situation is even more implicit when the metaclass is inherited
+from some base class, therefore lacking also the visual clue of the hook.
+
+For these reasons, metaclasses are something to be used with great care;
+they can easily make your code unreadable and confuse inexpert programmers.
+Moreover, it is more difficult to debug programs involving metaclasses, since
+methods are magically transformed by routines defined in the metaclass,
+and the code you see in the class is *not* what Python sees. I think
+the least confusing way of using metaclasses, is to concentrate all
+the dynamics on them and to write empty classes except for the
+metaclass hook. If you write a class with no methods such as
+
+ ::
+
+ class TransformedUglyDuckling(object):
+ __metaclass__=MagicallyTransformed
+
+then the only place to look at, is the metaclass. I have found extremely
+confusing to have some of the methods defined in the class and some in
+the metaclass, especially during debugging.
+
+Another point to make, is that the ``__metaclass__``
+hook should not be used to modify pre-existing classes,
+since it requires modifying the source code (even if it is enough to
+change one line only). Moreover, it is confusing, since adding a
+``__metaclass__`` attribute *after* the class creation would not do the job:
+
+ >>> from oopp import UglyDuckling, MagicallyTransformed
+ >>> UglyDuckling.__metaclass__=MagicallyTransformed
+ >>> print UglyDuckling()
+ "Not much, I am the class UglyDuckling"
+
+The reason is that we have to think of UglyDuckling as an instance of
+``type``, the built-in metaclasses; merely adding a ``__metaclass__``
+attribute does not re-initialize the class.
+The problem is elegantly solved by avoiding the hook and creating
+an enhanced copy of the original class trough ``MagicallyTransformed``
+used as a class factory.
+
+ >>> name=UglyDuckling.__name__
+ >>> bases=UglyDuckling.__bases__
+ >>> dic=UglyDuckling.__dict__.copy()
+ >>> UglyDuckling=MagicallyTransformed(name,bases,dic)
+
+Notice that I have recreated 'UglyDuckling', giving to the new class
+the old identifier.
+
+ >>> print UglyDuckling()
+ Very beautiful, since I am <UglyDuckling>>
+
+The metaclass of this new 'UglyDuckling' has been specified and will
+accompanies all future children of 'UglyDuckling':
+
+ >>> class Swan(UglyDuckling): pass
+ ...
+ >>> type(Swan)
+ <class '__main__.MagicallyTransformed'>
+
+Another caveat, is in the overridding of `` __init__`` in the metaclass.
+This is quite common in the case of metaclasses called trough the
+``__metaclass__`` hook mechanism, since in this case the class
+has been already defined (if not created) in the class statement,
+and we are interested in initializing it, more than in recreating
+it (which is still possible, by the way).
+The problem is that overriding ``__init__`` has severe limitations
+with respect to overriding ``__new__``,
+since the 'name', 'bases' and 'dic' arguments cannot be directly
+changed. Let me show an example:
+
+ ::
+
+ #<init_in_metaclass.py>
+
+ from oopp import *
+
+ class M(type):
+ "Shows that dic cannot be modified in __init__, only in __new__"
+ def __init__(cls,name,bases,dic):
+ name='C name cannot be changed in __init__'
+ bases='cannot be changed'
+ dic['changed']=True
+
+ class C(object):
+ __metaclass__=M
+ changed=False
+
+ print C.__name__ # => C
+ print C.__bases__ # => (<type 'object'>,)
+ print C.changed # => False
+
+ #</init_in_metaclass.py>
+
+The output of this script is ``False``: the dictionary cannot be changed in
+``__init__`` method. However, replacing ``dic['changed']=True`` with
+``cls.changed=True`` would work. Analougously, changing ``cls.__name__``
+would work. On the other hand, ``__bases__`` is a read-only attribute and
+cannot be changed once the class has been created, therefore there is no
+way it can be touched in ``__init__``. However, ``__bases__`` could be
+changed in ``__new__`` before the class creation.
+
+Metaclasses and inheritance
+-------------------------------------------------------------------------
+
+It is easy to get confused about the difference between a metaclass
+and a mix-in class in multiple inheritance, since
+both are denoted by adjectives and both share the same idea of
+enhancing a hierarchy. Moreover, both mix-in classes and metaclasses
+can be inherited in the whole hierarchy.
+Nevertheless, they behaves differently
+and there are various subtle point to emphasize. We have already
+noticed in the first section that attributes of a metaclass
+are transmitted to its instances, but not to the instances of the
+instances, whereas the normal inheritance is transitive: the
+grandfather transmits its attributes to the children and to the grandchild
+too. The difference can be represented with the following picture, where
+'M' is the metaclass, 'B' a base class, 'C' a children of 'B'
+and c an instance of 'C':
+
+ ::
+
+ M (attr) B (attr)
+ : |
+ C (attr) C (attr)
+ : :
+ c () c (attr)
+
+Notice that here the relation of instantiation is denoted by a dotted line.
+
+This picture is valid when C has metaclass M but not base class, on when C
+has base class but not metaclass. However, what happens whrn the class C has
+both a metaclass M and a base class B ?
+
+ >>> class M(type): a='M.a'
+ >>> class B(object): a='B.a'
+ >>> class C(B): __metaclass__=M
+ >>> c=C()
+
+The situation can be represented by in the following graph,
+
+ ::
+
+ (M.a) M B (B.a)
+ : /
+ : /
+ (?) C
+ :
+ :
+ (?) c
+
+
+Here the metaclass M and the base class B are fighting one against the other.
+Who wins ? C should inherit the attribute 'B.a' from its base B, however,
+the metaclass would like to induce an attribute 'M.a'.
+The answer is that the inheritance constraint wins on the metaclass contraint:
+
+ >>> C.a
+ 'B.a'
+ >>> c.a
+ 'B.a'
+
+The reason is the same we discussed in the fairy tale example: 'M.a' is
+an attribute of the metaclass, if its instance C has already a specified
+attributed C.a (in this case specified trough inheritance from B), then
+the attribute is not modified. However, one could *force* the modification:
+
+ >>> class M(type):
+ ... def __init__(cls,*args): cls.a='M.a'
+ >>> class C(B): __metaclass__=M
+ >>> C.a
+ 'M.a'
+
+In this case the metaclass M would win on the base class B. Actually,
+this is not surprising, since it is explicit. What could be surprising,
+had we not explained why inheritance silently wins, is that
+
+ >>> c.a
+ 'B.a'
+
+This explain the behaviour for special methods like
+``__new__,__init__,__str__``,
+etc. which are defined both in the class and the metaclass with the same
+name (in both cases,they are inherited from ``object``).
+
+In the chapter on objects, we learned that the printed representation of
+an object can be modified by overring the ``__str__`` methods of its
+class. In the same sense, the printed representation of a class can be
+modified by overring the ``__str__`` methods of its metaclass. Let me show an
+example:
+
+ ::
+
+ #<oopp.py>
+
+ class Printable(PrettyPrinted,type):
+ """Apparently does nothing, but actually makes PrettyPrinted acting as
+ a metaclass."""
+
+ #</oopp.py>
+
+Instances of 'Printable' are classes with a nice printable representation:
+
+ >>> from oopp import Printable
+ >>> C=Printable('Classname',(),{})
+ >>> print C
+ Classname
+
+However, the internal string representation stays the same:
+
+ >>> C # invokes Printable.__repr__
+ <class '__main__.Classname'>
+
+Notice that the name of class 'C' is ``Classname`` and not 'C' !
+
+Consider for instance the following code:
+
+ >>> class M(type):
+ ... def __str__(cls):
+ ... return cls.__name__
+ ... def method(cls):
+ ... return cls.__name__
+ ...
+ >>> class C(object):
+ ... __metaclass__=M
+ >>> c=C()
+
+In this case the ``__str__`` method in ``M`` cannot override the
+``__str__`` method in C, which is inherited from ``object``.
+Moreover, if you experiment a little, you will see that
+
+ >>> print C # is equivalent to print M.__str__(C)
+ C
+ >>> print c # is equivalent to print C.__str__(c)
+ <__main__.C object at 0x8158f54>
+
+
+The first ``__str__`` is "attached" to the metaclass and the
+second to the class.
+
+Consider now the standard method "method". It is both attached to the
+metaclass
+
+ >>> print M.method(C)
+ C
+
+and to the class
+
+ >>> print C.method() #in a sense, this is a class method, i.e. it receives
+ C #the class as first argument
+
+Actually it can be seen as a class method of 'C' (cfr. Guido van Rossum
+"Unifying types and classes in Python 2.2". When he discusses
+classmethods he says: *"Python also has real metaclasses, and perhaps
+methods defined in a metaclass have more right to the name "class method";
+but I expect that most programmers won't be using metaclasses"*). Actually,
+this is the SmallTalk terminology, Unfortunately, in Python the word
+``classmethod`` denotes an attribute descriptor, therefore it is better
+to call the methods defined in a metaclass *metamethods*, in order to avoid
+any possible confusion.
+
+The difference between ``method`` and ``__str__`` is that you cannot use the
+syntax
+
+ >>> print C.__str__() #error
+ TypeError: descriptor '__str__' of 'object' object needs an argument
+
+because of the confusion with the other __str__; you can only use the
+syntax
+
+ >>> print M.__str__(C)
+
+Suppose now I change C's definition by adding a method called "method":
+
+ ::
+
+ class C(object):
+ __metaclass__=M
+ def __str__(self):
+ return "instance of %s" % self.__class__
+ def method(self):
+ return "instance of %s" % self.__class__
+
+If I do so, then there is name clashing and the previously working
+statement print C.method() gives now an error:
+
+ ::
+
+ Traceback (most recent call last):
+ File "<stdin>", line 24, in ?
+ TypeError: unbound method method() must be called with C instance as
+ first argument (got nothing instead)
+
+Conclusion: ``__str__, __new__, __init__`` etc. defined in the metaclass
+have name clashing with the standard methods defined in the class, therefore
+they must be invoked with the extended syntax (ex. ``M.__str__(C)``),
+whereas normal methods in the metaclass with no name clashing with the methods
+of the class can be used as class methods (ex. ``C.method()`` instead of
+``M.method(C)``).
+Metaclass methods are always bound to the metaclass, they bind to the class
+(receiving the class as first argument) only if there is no name clashing with
+already defined methods in the class. Which is the case for ``__str__``,
+``___init__``, etc.
+
+Conflicting metaclasses
+----------------------------------------------------------------------------
+
+Consider a class 'A' with metaclass 'M_A' and a class 'B' with
+metaclass 'M_B'; suppose I derive 'C' from 'A' and 'B'. The question is:
+what is the metaclass of 'C' ? Is it 'M_A' or 'M_B' ?
+
+The correct answer (see "Putting metaclasses to work" for a thought
+discussion) is 'M_C', where 'M_C' is a metaclass that inherits from
+'M_A' and 'M_B', as in the following graph:
+
+
+ .. figure:: fig1.gif
+
+However, Python is not yet that magic, and it does not automatically create
+'M_C'. Instead, it will raise a ``TypeError``, warning the programmer of
+the possible confusion:
+
+ >>> class M_A(type): pass
+ >>> class M_B(type): pass
+ >>> A=M_A('A',(),{})
+ >>> B=M_B('B',(),{})
+ >>> class C(A,B): pass #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: metatype conflict among bases
+
+This is an example where the metaclasses 'M_A' and 'M_B' fight each other
+to generate 'C' instead of cooperating. The metatype conflict can be avoided
+by assegning the correct metaclass to 'C' by hand:
+
+ >>> class C(A,B): __metaclass__=type("M_AM_B",(M_A,M_B),{})
+ >>> type(C)
+ <class '__main__.M_AM_B'>
+
+In general, a class A(B, C, D , ...) can be generated without conflicts only
+if type(A) is a subclass of each of type(B), type(C), ...
+
+In order to avoid conflicts, the following function, that generates
+the correct metaclass by looking at the metaclasses of the base
+classes, is handy:
+
+ ::
+
+ #<oopp.py>
+
+ metadic={}
+
+ def _generatemetaclass(bases,metas,priority):
+ trivial=lambda m: sum([issubclass(M,m) for M in metas],m is type)
+ # hackish!! m is trivial if it is 'type' or, in the case explicit
+ # metaclasses are given, if it is a superclass of at least one of them
+ metabs=tuple([mb for mb in map(type,bases) if not trivial(mb)])
+ metabases=(metabs+metas, metas+metabs)[priority]
+ if metabases in metadic: # already generated metaclass
+ return metadic[metabases]
+ elif not metabases: # trivial metabase
+ meta=type
+ elif len(metabases)==1: # single metabase
+ meta=metabases[0]
+ else: # multiple metabases
+ metaname="_"+''.join([m.__name__ for m in metabases])
+ meta=makecls()(metaname,metabases,{})
+ return metadic.setdefault(metabases,meta)
+
+ #</oopp.py>
+
+This function is particularly smart since:
+
+ 1. Avoid duplications ..
+
+ 2. Remember its results.
+
+We may generate the child of a tuple of base classes with a given metaclass
+and avoiding metatype conflicts thanks to the following ``child`` function:
+
+ ::
+
+ #<oopp.py>
+
+ def makecls(*metas,**options):
+ """Class factory avoiding metatype conflicts. The invocation syntax is
+ makecls(M1,M2,..,priority=1)(name,bases,dic). If the base classes have
+ metaclasses conflicting within themselves or with the given metaclasses,
+ it automatically generates a compatible metaclass and instantiate it.
+ If priority is True, the given metaclasses have priority over the
+ bases' metaclasses"""
+
+ priority=options.get('priority',False) # default, no priority
+ return lambda n,b,d: _generatemetaclass(b,metas,priority)(n,b,d)
+
+ #</oopp.py>
+
+Here is an example of usage:
+
+ >>> class C(A,B): __metaclass__=makecls()
+ >>> print C,type(C)
+ <class 'oopp.AB_'> <class 'oopp._M_AM_B'>
+
+Notice that the automatically generated metaclass does not pollute the
+namespace:
+
+ >>> _M_A_M_B #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ NameError: name '_M_A_M_B' is not defined
+
+It can only be accessed as ``type(C)``.
+
+Put it shortly, the ``child`` function allows to generate a child from bases
+enhanced by different custom metaclasses, by generating under the hood a
+compatibile metaclass via multiple inheritance from the original metaclasses.
+However, this logic can only work if the original metaclasses are
+cooperative, i.e. their methods are written in such a way to avoid
+collisions. This can be done by using the cooperative the ``super`` call
+mechanism discussed in chapter 4.
+
+Cooperative metaclasses
+----------------------------------------------------------------------------
+
+In this section I will discuss how metaclasses can be composed with
+classes and with metaclasses, too. Since we will discuss even
+complicated hierarchies, it is convenient to have an utility
+routine printing the MRO of a given class:
+
+ ::
+
+ #<oopp.py>
+
+ def MRO(cls):
+ count=0; out=["MRO of %s:" % cls.__name__]
+ for c in cls.__mro__:
+ name=c.__name__
+ bases=','.join([b.__name__ for b in c.__bases__])
+ s=" %s - %s(%s)" % (count,name,bases)
+ if type(c) is not type: s+="[%s]" % type(c).__name__
+ out.append(s); count+=1
+ return '\n'.join(out)
+
+ #</oopp.py>
+
+Notice that ``MRO`` also prints the metaclass' name in square brackets, for
+classes enhanced by a non-trivial metaclass.
+
+Consider for instance the following hierarchy:
+
+ >>> from oopp import MRO
+ >>> class B(object): pass
+ >>> class M(B,type): pass
+ >>> class C(B): __metaclass__=M
+
+Here 'M' is a metaclass that inherits from 'type' and the base class 'B'
+and 'C' is both an instance of 'M' and a child of 'B'. The inheritance
+graph can be draw as
+
+ ::
+
+ object
+ / \
+ B type
+ | \ /
+ | M
+ \ :
+ \ :
+ C
+
+Suppose now we want to retrieve the ``__new__`` method of B's superclass
+with respect to the MRO of C: obviously, this is ``object.__new__``, since
+
+ >>> print MRO(C)
+ MRO of C:
+ 0 - C(B)[M]
+ 1 - B(object)
+ 2 - object()
+
+This allows to create an instance of 'C' in this way:
+
+ >>> super(B, C).__new__(C)
+ <__main__.C object at 0x4018750c>
+
+It is interesting to notice that this would not work in Python 2.2,
+due to a bug in the implementation of ``super``, therefore do not
+try this trick with older version of Python.
+
+Notice that everything works
+only because ``B`` inherits the ``object.__new__`` staticmethod that
+is cooperative and it turns out that it calls ``type.__new__``. However,
+if I give to 'B' a non-cooperative method
+
+ >>> B.__new__=staticmethod(lambda cls,*args: object.__new__(cls))
+
+things do not work:
+
+ >>> M('D',(),{}) #error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ File "<stdin>", line 1, in <lambda>
+ TypeError: object.__new__(M) is not safe, use type.__new__()
+
+A cooperative method would solve the problem:
+
+ >>> B.__new__=staticmethod(lambda m,*args: super(B,m).__new__(m,*args))
+ >>> M('D',(),{}) # calls B.__new__(M,'D',(),{})
+ <class '__main__.D'>
+
+Cooperation between classes and metaclasses is pretty tricky.
+This is why autosuper is so difficult to get right.
+
+Consider this example, with object and type instances of MetaSuper::
+
+ class B(object):
+ def __init__(self, *args):
+ print "B.__init__"
+ self.__super.__init__(*args)
+
+ class M(B, type):
+ def __init__(self, n, b, d):
+ print "M.__init__"
+ self.__super.__init__(n, b, d)
+
+ class C(B):
+ __metaclass__ = M
+ def __init__(self):
+ print "C.__init__"
+ self.__super.__init__()
+
+What happens here at C creation? The metaclass _MMetaSuper is generated,
+with ancestors M, B, superobject, supertype, MetaSuper, safetype, type,
+which calls M.__init_(C, )_ and then B.__init__(C) which calls
+the *unbound* method superobject.__init__(C ..) and NOT the *bound* method
+MetaSuper.__init__(C, ) which would be the "right" thing to do in this
+situation (but not in other situations). So C._C__super is NOT initialized :-(
+
+Metamethods vs class methods
+-------------------------------------------------------------------
+
+Meta-methods, i.e. methods defined in
+a metaclass.
+
+Python has already few built-in metamethods such as ``.mro()``,
+ ``__subclass__()``, ``__delattr__`` and possibly others.
+Moreover ``__name__`` is an example of meta-descriptor.
+These are methods of the metaclass 'type' and there of any of its
+sub-metaclasses.
+
+ >>> dir(type)
+ ['__base__', '__bases__', '__basicsize__', '__call__', '__class__',
+ '__cmp__', '__delattr__', '__dict__', '__dictoffset__', '__doc__',
+ '__flags__', '__getattribute__', '__hash__', '__init__', '__itemsize__',
+ '__module__', '__mro__', '__name__', '__new__', '__reduce__', '__repr__',
+ '__setattr__', '__str__', '__subclasses__', '__weakrefoffset__', 'mro']
+
+
+ >>> print type.mro.__doc__
+ mro() -> list
+ return a type's method resolution order
+ >>> print type.__subclasses__.__doc__
+ __subclasses__() -> list of immediate subclasses
+
+ >>> class A(object): pass
+ >>> class B(A): pass
+ >>> B.mro()
+ [<class '__main__.B'>, <class '__main__.A'>, <type 'object'>]
+ >>> A.__subclasses__()
+ [<class '__main__.B'>]
+
+Notice that ``mro()`` and ``__subclasses__`` are not retrieved by ``dir``.
+
+Let me constrast metamethods with the more traditional classmethods.
+In many senses, the to concepts are akin:
+
+ >>> class M(type):
+ ... "Metaclass with a (meta)method mm"
+ ... def mm(cls): return cls
+ >>> D=M('D',(),{'cm':classmethod(lambda cls: cls)})
+ >>> # instance of M with a classmethod cm
+ >>> D.mm # the metamethod
+ <bound method M.mm of <class '__main__.C'>>
+ >>> D.cm # the classmethod
+ <unbound method D.<lambda>>
+
+Notice the similarities between the classmethod and the metamethod:
+
+ >>> D.mm.im_self, D.cm.im_self # the same
+ (<class '__main__.D'>, <class '__main__.D'>)
+ >>> D.mm.im_class, D.cm.im_class # still the same
+ (<class '__main__.M'>, <class '__main__.M'>)
+
+There are no surprises for ``im_func``:
+
+ >>> D.mm.im_func, D.cm.im_func
+ (<function mm at 0x402c272c>, <function <lambda> at 0x402c280c>)
+
+Nevertheless, there are differences: metamethods are not bounded to
+instances of the class
+
+ >>> D().cm() # the classmethod works fine
+ <class '__main__.D'>
+ >>> D().mm() # the metamethod does not: error
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ AttributeError: 'D' object has no attribute 'mm'
+
+and they are not retrieved by ``dir``:
+
+ >>> from oopp import *
+ >>> attributes(D).keys() # mm is not retrieved, only cm
+ ['cm']
+
+ >>> cm.__get__('whatever') #under Python 2.2.0 would give a serious error
+ Segmentation fault
+ >>> cm.__get__(None) #under Python 2.3 there is no error
+ <bound method type.<lambda> of <type 'NoneType'>>
+
+Moreover metamethods behaves differently with respect to multiple
+inheritance. If a class A define a classmethod cA and a class B
+defines a classmethod cB, then the class C(A,B) inherits both the
+classmethods cA and cB. In the case of metamethods defined in M_A
+and M_B, the same is true only if one resolves the meta-type
+conflict by hand, by generating the metaclass M_C(M_A,M_B). In this
+sense, classmethods are simpler to use than metamethods.