Bruce Eckel avatar Bruce Eckel committed 7c3729d

Rough draft of Metaclasses chapter.

Comments (0)

Files changed (14)

Binary file added.

 syntax: glob
 *.pyc
 *.pyo
+*.class
 *~
 *-
 #*

Binary file added.

code/Jython/Fractal.py

-import turtle
-turtle.clear
-
-class fractal(object):
-
-     def __init__(self, lstring, rule, line_length, angle, shrink_factor):
-         self.lstring = lstring
-         self.rule = rule
-         self.line_length = line_length
-         self.angle = angle
-         self.shrink_factor=shrink_factor
-
-     def draw(self):
-         drawingdict = {'F': lambda: turtle.forward(self.line_length),
-                        '-':lambda: turtle.right(self.angle),
-                        '+':lambda: turtle.left(self.angle),}
-         for rule in self.lstring:
-             drawingdict[rule]()
-
-     def iterate(self):
-         self.lstring = self.lstring.replace(rule[0],rule[1])
-         self.line_length=self.line_length/self.shrink_factor
-
-def recurse(f,smallest):
-     if f.line_length>=smallest:
-         turtle.reset()
-         f.iterate()
-         f.draw()
-         recurse(f,smallest)
-
-if __name__=='__main__':
-     start = 'F+F+F+F'
-     rule = ('F','F+F-F-FF+F+F-F')
-     f = fractal(start,rule,50,90,2)
-     recurse(f,10)

code/Jython/test.html

-
-<html><body>
-<h1>Hello, World!</h1>
-</body></html>

code/Metaclasses/MyList.py

+# Metaclasses/MyList.py
+
+def howdy(self, you):
+    print("Howdy, " + you)
+
+MyList = type('MyList', (list,), dict(x=42, howdy=howdy))
+
+ml = MyList()
+ml.append("Camembert")
+print(ml)
+print(ml.x)
+ml.howdy("John")
+

code/Metaclasses/NewVSInit.py

+# Metaclasses/NewVSInit.py
+from pprint import pprint
+
+class Tag1: pass
+class Tag2: pass
+class Tag3:
+    def tag3_method(self): pass
+
+class MetaBase(type):
+    def __new__(mcl, name, bases, nmspc):
+        print('MetaBase.__new__\n')
+        return super(MetaBase, mcl).__new__(mcl, name, bases, nmspc)
+
+    def __init__(cls, name, bases, nmspc):
+        print('MetaBase.__init__\n')
+        super(MetaBase, cls).__init__(name, bases, nmspc)
+
+class MetaNewVSInit(MetaBase):
+    def __new__(mcl, name, bases, nmspc):
+        # First argument is the metaclass ``MetaNewVSInit``
+        print('MetaNewVSInit.__new__')
+	for x in (mcl, name, bases, nmspc): pprint(x)
+        print('')
+        # These all work because the class hasn't been created yet:
+        if 'foo' in nmspc: nmspc.pop('foo')
+        name += '_x'
+        bases += (Tag1,)
+        nmspc['baz'] = 42
+        return super(MetaNewVSInit, mcl).__new__(mcl, name, bases, nmspc)
+
+    def __init__(cls, name, bases, nmspc):
+        # First argument is the class being initialized
+        print('MetaNewVSInit.__init__')
+	for x in (cls, name, bases, nmspc): pprint(x)
+        print('')
+        if 'bar' in nmspc: nmspc.pop('bar') # No effect
+        name += '_y' # No effect
+        bases += (Tag2,) # No effect
+        nmspc['pi'] = 3.14159 # No effect
+        super(MetaNewVSInit, cls).__init__(name, bases, nmspc)
+        # These do work because they operate on the class object:
+        cls.__name__ += '_z'
+        cls.__bases__ += (Tag3,)
+        cls.e = 2.718
+
+class Test(object):
+    __metaclass__ = MetaNewVSInit
+    def __init__(self):
+        print('Test.__init__')
+    def foo(self): print('foo still here')
+    def bar(self): print('bar still here')
+
+t = Test()
+print('class name: ' + Test.__name__)
+print('base classes: ', [c.__name__ for c in Test.__bases__])
+print([m for m in dir(t) if not m.startswith("__")])
+t.bar()
+print(t.e)
+
+""" Output:
+MetaNewVSInit.__new__
+<class '__main__.MetaNewVSInit'>
+'Test'
+(<type 'object'>,)
+{'__init__': <function __init__ at 0x7ecf0>,
+ '__metaclass__': <class '__main__.MetaNewVSInit'>,
+ '__module__': '__main__',
+ 'bar': <function bar at 0x7ed70>,
+ 'foo': <function foo at 0x7ed30>}
+
+MetaBase.__new__
+
+MetaNewVSInit.__init__
+<class '__main__.Test_x'>
+'Test'
+(<type 'object'>,)
+{'__init__': <function __init__ at 0x7ecf0>,
+ '__metaclass__': <class '__main__.MetaNewVSInit'>,
+ '__module__': '__main__',
+ 'bar': <function bar at 0x7ed70>,
+ 'baz': 42}
+
+MetaBase.__init__
+
+Test.__init__
+class name: Test_x_z
+('base classes: ', ['object', 'Tag1', 'Tag3'])
+['bar', 'baz', 'e', 'tag3_method']
+bar still here
+2.718
+"""

code/Metaclasses/RegisterLeafClasses.py

+# Metaclasses/RegisterLeafClasses.py
+
+class ClassSet(set):
+    "Simplify printing a set of classes"
+    def __str__(self):
+        return "(" + ", ".join([c.__name__ for c in self]) + ")"
+
+class RegisterLeafClasses(type):
+    def __init__(cls, name, bases, nmspc):
+        super(RegisterLeafClasses, cls).__init__(name, bases, nmspc)
+        if not hasattr(cls, 'registry'):
+            cls.registry = ClassSet()
+        cls.registry.add(cls)
+        cls.registry -= set(bases) # Remove base classes
+
+class Color(object):
+    __metaclass__ = RegisterLeafClasses
+
+class Blue(Color): pass
+class Red(Color): pass
+class Green(Color): pass
+class Yellow(Color): pass
+print(Color.registry)
+class PhthaloBlue(Blue): pass
+class CeruleanBlue(Blue): pass
+print(Color.registry)
+
+class Shape(object):
+    __metaclass__ = RegisterLeafClasses
+
+class Round(Shape): pass
+class Square(Shape): pass
+class Triangular(Shape): pass
+class Boxy(Shape): pass
+print(Shape.registry)
+class Circle(Round): pass
+class Ellipse(Round): pass
+print(Shape.registry)
+
+""" Output:
+(Red, Blue, Yellow, Green)
+(Red, CeruleanBlue, Yellow, PhthaloBlue, Green)
+(Square, Round, Boxy, Triangular)
+(Square, Ellipse, Boxy, Circle, Triangular)
+"""

code/Metaclasses/SimpleMeta1.py

+# Metaclasses/SimpleMeta1.py
+# Two-step metaclass creation in Python 2.x
+
+class SimpleMeta1(type):
+    def __init__(cls, name, bases, nmspc):
+        super(SimpleMeta1, cls).__init__(name, bases, nmspc)
+        cls.uses_metaclass = lambda self : "Yes!"
+
+class Simple1(object):
+    __metaclass__ = SimpleMeta1
+    def foo(self): pass
+    @staticmethod
+    def bar(): pass
+
+simple = Simple1()
+print([m for m in dir(simple) if not m.startswith('__')])
+# A new method has been injected by the metaclass:
+print simple.uses_metaclass()
+
+""" Output:
+['bar', 'foo', 'uses_metaclass']
+Yes!
+"""

code/Metaclasses/SimpleMeta2.py

+# Metaclasses/SimpleMeta2.py
+# Combining the steps for metaclass creation in Python 2.x
+
+class Simple2(object):
+    class __metaclass__(type):
+        def __init__(cls, name, bases, nmspc):
+            # This won't work:
+            # super(__metaclass__, cls).__init__(name, bases, nmspc)
+            # Less-flexible specific call:
+            type.__init__(cls, name, bases, nmspc)
+            cls.uses_metaclass = lambda self : "Yes!"
+
+class Simple3(Simple2): pass
+simple = Simple3()
+print simple.uses_metaclass()
+
+""" Output:
+Yes!
+"""

code/Metaclasses/SimpleMeta3.py

+# Metaclasses/SimpleMeta3.py
+# A function for __metaclass__ in Python 2.x
+
+class Simple4(object):
+    def __metaclass__(name, bases, nmspc):
+        cls = type(name, bases, nmspc)
+        cls.uses_metaclass = lambda self : "Yes!"
+        return cls
+
+simple = Simple4()
+print simple.uses_metaclass()
+
+""" Output:
+Yes!
+"""

Binary file added.

src/Metaclasses.rst

 when we write a class, the default metaclass ``type`` is automatically
 invoked to create that class, and we aren't even aware that it's happening. 
 
-It's possible to explicitly code the metaclass' creation of the
-class. The arguments when invoking ``type`` are the name of the class,
+It's possible to explicitly code the metaclass' creation of a
+class. ``type`` called with one argument produces the type information
+of an existing class; ``type`` called with three arguments creates a
+new class object. The arguments when invoking ``type`` are the name of the class,
 a list of base classes, and a dictionary giving the namespace for the
 class (all the fields and methods). So the equivalent of::
 
 sensibly: you're calling a function that creates a new type based on
 its arguments.
 
+We can also add base classes, fields and methods::
+
+    # Metaclasses/MyList.py
+
+    def howdy(self, you):
+        print("Howdy, " + you)
+
+    MyList = type('MyList', (list,), dict(x=42, howdy=howdy))
+
+    ml = MyList()
+    ml.append("Camembert")
+    print(ml)
+    print(ml.x)
+    ml.howdy("John")
+
 The ability to generate classes programmatically using ``type`` opens
 up some interesting possibilities. Consider the GreenHouseLanguage.py
 example in the Jython chapter -- all the subclasses in that case were
 ``Event``. Note that the resulting class must be inserted into the
 global namespace, otherwise it will not be seen.
 
-This approach works fine, but then consider the following
+This approach works fine, but then consider the subsequent
 ``create_exec()`` function, which accomplishes the same thing by
 calling ``exec`` on a string defining the class. This will be much
 easier to understand by the vast majority of the people reading your
-code -- those who do not understand metaclasses.
+code: those who do not understand metaclasses.
 
-Python 3 metaclasses (and does the Python 2 syntax still work in 3?)
+The Metaclass Hook
+================================================================================
 
-example: Autogeneration of the "event" sublclasses in the greenhouse
-example using type(), and metaclass programming. Alternative, show
-class decorator if possible. Or even exec?
+So far, we've only used ``type`` metaclass directly. Metaclass
+programming involves hooking our own operations into the creation of
+class objects. This is accomplished by:
+
+      1. Writing a subclass of the metaclass ``type``.
+      2. Inserting the new metaclass into the class creation process
+         using the *metaclass hook*.
+
+In Python 2.x, the metaclass hook is a static field in the class
+called ``__metaclass__``. In the ordinary case, this is not assigned
+so Python just uses ``type`` to create the class. But if you define
+``__metaclass__`` to point to a callable that takes four arguments,
+Python will call ``__metaclass__()`` after the initial creation of the
+class object, passing in the class object, the class name, the list of
+base classes and the namespace dictionary.
+
+Thus, the basic process of metaclass programming looks like this::
+
+    # Metaclasses/SimpleMeta1.py
+    # Two-step metaclass creation in Python 2.x
+
+    class SimpleMeta1(type):
+        def __init__(cls, name, bases, nmspc):
+            super(SimpleMeta1, cls).__init__(name, bases, nmspc)
+            cls.uses_metaclass = lambda self : "Yes!"
+
+    class Simple1(object):
+        __metaclass__ = SimpleMeta1
+        def foo(self): pass
+        @staticmethod
+        def bar(): pass
+
+    simple = Simple1()
+    print([m for m in dir(simple) if not m.startswith('__')])
+    # A new method has been injected by the metaclass:
+    print simple.uses_metaclass()
+
+    """ Output:
+    ['bar', 'foo', 'uses_metaclass']
+    Yes!
+    """
+
+By convention, when defining metaclasses ``cls`` is used rather than
+``self`` as the first argument to all methods except ``__new__()``
+(which uses ``mcl``, for reasons explained later). ``cls``
+is the class object that is being modified.
+
+Note that the practice of calling the base-class constructor first (via
+super()) in the derived-class constructor should be followed with
+metaclasses as well.
+
+``__metaclass__`` only needs to be callable, so in Python
+2.x it's possible to define ``__metaclass__`` inline::
+
+    # Metaclasses/SimpleMeta2.py
+    # Combining the steps for metaclass creation in Python 2.x
+
+    class Simple2(object):
+        class __metaclass__(type):
+            def __init__(cls, name, bases, nmspc):
+                # This won't work:
+                # super(__metaclass__, cls).__init__(name, bases, nmspc)
+                # Less-flexible specific call:
+                type.__init__(cls, name, bases, nmspc)
+                cls.uses_metaclass = lambda self : "Yes!"
+
+    class Simple3(Simple2): pass
+    simple = Simple3()
+    print simple.uses_metaclass()
+
+    """ Output:
+    Yes!
+    """
+
+The compiler won't accept the ``super()`` call because it says
+``__metaclass__`` hasn't been defined, forcing us to use the specific
+call to ``type.__init__()``. 
+
+Because it only needs to be callable, it's even possible to define
+``__metaclass__`` as a function::
+
+    # Metaclasses/SimpleMeta3.py
+    # A function for __metaclass__ in Python 2.x
+
+    class Simple4(object):
+        def __metaclass__(name, bases, nmspc):
+            cls = type(name, bases, nmspc)
+            cls.uses_metaclass = lambda self : "Yes!"
+            return cls
+
+    simple = Simple4()
+    print simple.uses_metaclass()
+
+    """ Output:
+    Yes!
+    """
+
+As you'll see, Python 3 doesn't allow the syntax of these last two
+examples. Even so, the above example makes it quite clear what's
+happening: the class object is created, then modified, then returned.
+
+.. Note:: Or does it?
+
+
+The Metaclass Hook in Python 3
+----------------------------------------------------------------------
+
+Python 3 changes the metaclass hook. It doesn't disallow the
+``__metaclass__`` field, but it ignores it. Instead, you use a keyword
+argument in the base-class list:
+
+This means that none of the (clever) alternative ways of defining
+``__metaclass__`` as a class or function are available in
+Python 3. All metaclasses must be defined as separate classes. This is
+probably just as well, as it makes metaclass programs more consistent
+and thus easier to read and understand.
+
+
+
 
 .. Possible example: simplification of XML creation via operator
    overloading.
 
->>> C = type('C', (object,), {})
->>> c = C()
->>> C
-
 
 Example: Self-Registration of Subclasses
 ================================================================================
 mechanism -- each sublclass become an element of a group that you work
 on. For example, in the **CodeManager.py** example in the
 **Comprehensions** chapter, the subclasses of **Language** were all
-the languages that needed to be processed.
+the languages that needed to be processed. Each **Language** subclass
+described the traits of that language.
 
-To achieve this, you need to somehow keep a list of all the subclasses
+As a simple example, let's consider a system that automatically keeps
+a list of all of it's "leaf" subclasses (only the classes that have no
+inheritors). This is useful when you are using the class hierarchy as
+a structure to keep track of a group of types, so you can easily
+enumerate through all the subtypes.
+
+To achieve this, you need to somehow keep a list of all subclasses
 that are inherited from your base class, so you can iterate through
 and perform processing for each one. One way of keeping track
-automatially is to use metaclasses.
+automatially is to use metaclasses::
 
-untested, will require rewriting::
+    # Metaclasses/RegisterLeafClasses.py
 
-    class Base(object):
-        registry = []
-        class __metaclass__(type):
-            def __init__(cls, name, bases, dict):
-		cls.registry.append(cls)
-	def __new__(cls, a):
-	    if cls != Base:
-		return object.__new__(cls, a)
-	    for subcls in cls.registry:
-		if subcls == Base:
-		    continue
-		try:
-		    return subcls(a)
-		except ValueError:
-		    pass
-	    raise ValueError("No subclass found")
-	def __init__(self, input):
-	    super(Base, self).__init__(input)
-	    self.data = input
+    class ClassSet(set):
+        "Simplify printing a set of classes"
+        def __str__(self):
+            return "(" + ", ".join([c.__name__ for c in self]) + ")"
 
-    class Derived1(Base):
-	def __init__(self, s):
-	    s = int(s)
-	    super(Derived1, self).__init__(s)
-	    self.s = s
+    class RegisterLeafClasses(type):
+        def __init__(cls, name, bases, nmspc):
+            super(RegisterLeafClasses, cls).__init__(name, bases, nmspc)
+            if not hasattr(cls, 'registry'):
+                cls.registry = ClassSet()
+            cls.registry.add(cls)
+            cls.registry -= set(bases) # Remove base classes
 
-    class Derived2(Base):
-	def __init__(self, s):
-	    if ',' not in s:
-		raise ValueError("Not a list")
-	    super(Derived2, self).__init__(s)
-	    self.s = s.split(',')
+    class Color(object):
+        __metaclass__ = RegisterLeafClasses
 
-    class Derived3(Base):
-	pass 
+    class Blue(Color): pass
+    class Red(Color): pass
+    class Green(Color): pass
+    class Yellow(Color): pass
+    print(Color.registry)
+    class PhthaloBlue(Blue): pass
+    class CeruleanBlue(Blue): pass
+    print(Color.registry)
 
-Here's another version which seems a lot simpler::
+    class Shape(object):
+        __metaclass__ = RegisterLeafClasses
 
-    class LeafClassesMeta(type):
-        """
-        A metaclass for classes that keeps track of all of them that
-        aren't base classes.
-        """
+    class Round(Shape): pass
+    class Square(Shape): pass
+    class Triangular(Shape): pass
+    class Boxy(Shape): pass
+    print(Shape.registry)
+    class Circle(Round): pass
+    class Ellipse(Round): pass
+    print(Shape.registry)
 
-        registry = set()
+    """ Output:
+    (Red, Blue, Yellow, Green)
+    (Red, CeruleanBlue, Yellow, PhthaloBlue, Green)
+    (Square, Round, Boxy, Triangular)
+    (Square, Ellipse, Boxy, Circle, Triangular)
+    """
 
-        def __init__(cls, name, bases, attrs):
-            cls.registry.add(cls)
-            # remove any base classes
-            cls.registry -= set(bases)
 
-But it also seems like you can do it more simply with the denser syntax::
-
-    class Base(object):
-        """
-        A metaclass for classes that keeps track of all of them that
-        aren't base classes.
-        """
-
-        registry = set()
-
-        def __metaclass__(name, bases, attrs):
-            cls.registry.add(cls)
-            # remove any base classes
-            cls.registry -= set(bases)
-
-Apparently, ``__metaclass__`` just needs to be callable, so it doesn't
-matter whether it's a class with an __init__ or a function. Of course
-this won't work in Python 3 where the ``__metaclass__`` field is ignored.
 
 Using Class Decorators
 --------------------------------------------------------------------------------
 (As in the Comprehensions chapter)
 
 
+Using ``__init__`` vs. ``__new__`` in Metaclasses
+================================================================================
+
+It can be confusing when you see metaclass examples that appear to
+arbitrarily use ``__new__`` or ``__init__`` -- why choose one over the other?
+
+``__new__`` is called for the creation of a new class, while
+``__init__`` is called after the class is created, to perform
+additional initialization before the class is handed to the caller::
+
+    # Metaclasses/NewVSInit.py
+    from pprint import pprint
+
+    class Tag1: pass
+    class Tag2: pass
+    class Tag3:
+        def tag3_method(self): pass
+
+    class MetaBase(type):
+        def __new__(mcl, name, bases, nmspc):
+            print('MetaBase.__new__\n')
+            return super(MetaBase, mcl).__new__(mcl, name, bases, nmspc)
+
+        def __init__(cls, name, bases, nmspc):
+            print('MetaBase.__init__\n')
+            super(MetaBase, cls).__init__(name, bases, nmspc)
+
+    class MetaNewVSInit(MetaBase):
+        def __new__(mcl, name, bases, nmspc):
+            # First argument is the metaclass ``MetaNewVSInit``
+            print('MetaNewVSInit.__new__')
+            for x in (mcl, name, bases, nmspc): pprint(x)
+            print('')
+            # These all work because the class hasn't been created yet:
+            if 'foo' in nmspc: nmspc.pop('foo')
+            name += '_x'
+            bases += (Tag1,)
+            nmspc['baz'] = 42
+            return super(MetaNewVSInit, mcl).__new__(mcl, name, bases, nmspc)
+
+        def __init__(cls, name, bases, nmspc):
+            # First argument is the class being initialized
+            print('MetaNewVSInit.__init__')
+            for x in (cls, name, bases, nmspc): pprint(x)
+            print('')
+            if 'bar' in nmspc: nmspc.pop('bar') # No effect
+            name += '_y' # No effect
+            bases += (Tag2,) # No effect
+            nmspc['pi'] = 3.14159 # No effect
+            super(MetaNewVSInit, cls).__init__(name, bases, nmspc)
+            # These do work because they operate on the class object:
+            cls.__name__ += '_z'
+            cls.__bases__ += (Tag3,)
+            cls.e = 2.718
+
+    class Test(object):
+        __metaclass__ = MetaNewVSInit
+        def __init__(self):
+            print('Test.__init__')
+        def foo(self): print('foo still here')
+        def bar(self): print('bar still here')
+
+    t = Test()
+    print('class name: ' + Test.__name__)
+    print('base classes: ', [c.__name__ for c in Test.__bases__])
+    print([m for m in dir(t) if not m.startswith("__")])
+    t.bar()
+    print(t.e)
+
+    """ Output:
+    MetaNewVSInit.__new__
+    <class '__main__.MetaNewVSInit'>
+    'Test'
+    (<type 'object'>,)
+    {'__init__': <function __init__ at 0x7ecf0>,
+     '__metaclass__': <class '__main__.MetaNewVSInit'>,
+     '__module__': '__main__',
+     'bar': <function bar at 0x7ed70>,
+     'foo': <function foo at 0x7ed30>}
+
+    MetaBase.__new__
+
+    MetaNewVSInit.__init__
+    <class '__main__.Test_x'>
+    'Test'
+    (<type 'object'>,)
+    {'__init__': <function __init__ at 0x7ecf0>,
+     '__metaclass__': <class '__main__.MetaNewVSInit'>,
+     '__module__': '__main__',
+     'bar': <function bar at 0x7ed70>,
+     'baz': 42}
+
+    MetaBase.__init__
+
+    Test.__init__
+    class name: Test_x_z
+    ('base classes: ', ['object', 'Tag1', 'Tag3'])
+    ['bar', 'baz', 'e', 'tag3_method']
+    bar still here
+    2.718
+    """
+
+
+The primary difference is that when overriding __new__ you can change
+things like the 'name', 'bases' and 'namespace' arguments before you
+call the super constructor and it will have an effect, but doing the
+same thing in __init__ you won't get any results from the constructor
+call.
+
+One special case in __new__ is that you can
+manipulate things like ``__slots__``, but in ``__init__()`` you can't.
+
+Note that, since the base-class version of __init__ doesn't make any
+modifications, it makes sense to call it first, then perform any
+additional operations. In C++ and Java, the base-class constructor
+*must* be called as the first operation in a derived-class
+constructor, which makes sense because derived-class constructions can
+then build upon base-class foundations.
+
+In many cases, the choice of __new__ vs __init__ is a style issue and
+doesn't matter, but because __new__ can do everything and __init__ is
+slightly more limited, some people just start using __new__ and stick with
+it. Because the use can be confusing, I prefer to only use __new__
+when it has meaning -- when you must in order to change things that
+only __new__ can change. 
+
 Class Methods and Metamethods
 ================================================================================
 
 
 (Is a similar relationship true with attributes, or is it different?)
 
+Intercepting Class Creation
+--------------------------------------------------------------------------------
+
+This example implements singleton using metaclasses, by overriding the
+``__call__()`` metamethod, which is invoked when a new instance is
+created::
+
+    class Singleton(type):
+    	instance = None # I don't think this will work; needs to be
+		     	# attched to the class.
+    	def __call__(cls, *args, **kw):
+            if not cls.instance:
+              	 cls.instance = super(Singleton, cls).__call__(*args, **kw)
+	    return cls.instance
+
+    class ASingleton(object):
+        __metaclass__ == Singleton
+
+    a = ASingleton()
+    b = ASingleton()
+    assert a is b
+
+By overriding ``__call__()`` in the metaclass, the creation of
+instances are intercepted. Instance creation is bypassed if one
+already exists.
+
+.. class decorator version that modifies __new__().
+
 Further Reading
 ================================================================================
 
-    http://cleverdevil.org/computing/78/ -- an excellent step-by-step
-    introduction to metaclasses.
+    Excellent step-by-step introduction to metaclasses:
+    http://cleverdevil.org/computing/78/
         
-    http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/ --
     Metaclass intro and comparison of syntax between Python 2.x and
-    3.x.
+    3.x:
+    http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/
 
+    David Mertz's metaclass primer:
+    http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html
+
+    Three-part in-depth coverage of metaclasses on IBM Developer
+    Works. Quite useful and authoritative:
     http://www.ibm.com/developerworks/linux/library/l-pymeta.html
     http://www.ibm.com/developerworks/linux/library/l-pymeta2/
     http://www.ibm.com/developerworks/linux/library/l-pymeta3.html
-    Three-part in-depth coverage of metaclasses on IBM Developer
-    Works. Quite useful and authoritative.
 
     Michele Simionato's articles on Artima, with special emphasis on
     the difference between Python 2.x and 3.x metaclasses:
     http://www.artima.com/weblogs/viewpost.jsp?thread=236234
     http://www.artima.com/weblogs/viewpost.jsp?thread=236260
 
+    Once you understand the foundations, you can find lots of examples
+    by searching for "metaclass" within the Python Cookbook:
+    http://code.activestate.com/recipes/langs/python/
+
+    The printed version of the Python Cookbook has far fewer examples
+    than the online version, but the print version has been filtered
+    and edited and so tends to be more authoritative.
+
+    Ian Bicking writes about metaclasses:
+    http://blog.ianbicking.org/a-conservative-metaclass.html
+    http://blog.ianbicking.org/metaclass-fun.html
+    http://blog.ianbicking.org/A-Declarative-Syntax-Extension.html
+    http://blog.ianbicking.org/self-take-two.html
+
     For more advanced study, the book `Putting Metaclasses to Work
-    <http://www.pearsonhighered.com/educator/academic/product/0,,0201433052,00%2ben-USS_01DBC.html>`_
-    .
+    <http://www.pearsonhighered.com/educator/academic/product/0,,0201433052,00%2ben-USS_01DBC.html>`_.
 

src/QuickPython.rst

 automatically get the equivalent of templates - without having to learn that
 particularly difficult syntax and semantics.
 
-.. note ::  (Reader) I am not sure if I agree with the remark about templates. One of the
+..   (Reader) I am not sure if I agree with the remark about templates. One of the
             big objective of templates has always been type safety along with
             genericity. What python gives us is the genericity. IMHO the analogy
             with template mechanism is not appropriate.
 
+Constructor Calls
+-------------------------------------------------------------------------------
+
+Automatic base-class constructor calls.
+
+Calling the base-class constructor first, how to do it using super(), why you
+should always call it first even if it's optional when to call it.
+
+.. guideline: Be rigorous about calling base-class initializers as the
+.. first step of your __init__() method. Call them using super() so
+.. that modifications to the class hierarchy don't cause problems.
+
 Static Fields
 -------------------------------------------------------------------------------
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.