[Python-3000] PEP 3115: Actual use cases for odd metaclasses? Alternate syntax.

Charles Merriam charles.merriam at gmail.com
Sat Dec 22 07:30:07 CET 2007


Hello all,

My first post to the mailing list, so pardon if I open a can of worms.
I know everyone groans whenever a metaclass is mentioned.

I gave a talk last week that included Metaclasses.  From what I've
read, there seems to be only one use case for metaclasses:  go through
and play with the attributes when the class is created.

That is, the use cases are:
1.   Go through the attributes on creation, in order, and create some
new attribute entries that call my IDL, XML, object database, or other
mapper.    For example, use a metaclass that looks at all class data
attributes and adds a "save_as_XML" function that writes these
correctly.
2.  Go through the attributes on creation, and throw exceptions if
some attributes are not specified, or if other attributes are
specified.  For example, require that all my methods include
docstrings and that each class have a "project_home_URI' data member
of type string.
3.  Go through the attributes on creation.  Add some common functions
all classes from this metaclass must have.  For example, all these
classes get the same "print_license" function.
4.  Go through the attributes on creation.  Apply decorators to most
methods to enforce security, logging, lazy loading, or persistence
management.  For example, all public functions get decorated with the
"@require_admin_access" decorator.

Are there any other use cases?   So far, there seems to be a lot of
boiler plate code to get here.  PEP 3115 adds some extra non-intuitive
code for specifying an alternate container which could be anything,
but will always be an array instead of a dict.

If there are no other use cases, then the syntax and implementation
could be made much simpler.   The usual boilerplate code looks like
this:

class Midnight_Hack(type):
 def __new__(cls, name, bases, attributes):
   for a,v in attributes.items():
         # process attributes here....
   return type.__new__(cls, name, bases,attributes)

So, instead we could make a routine that gets called from the 'type'
class directly.  It can have handy convenience functions, and has can
have more understandable semantics when classes inherit from multiple
base classes that each use metaclasses.   Here's a proposed syntax
sample:

def print_license:
    print "This is mine, mine, mine!"

def process(mc):   #mc for "meta class info"
       # I could just get and set mc.attr_list, but let's use
convenience functions

       mc.require_attribute("IDL_Version")
       # Might throw a Missing_Metaclass_Requirement_Exception

       mc.add_attr"print_license", print_license)   # every class needs this
       mc.add_attr("swizzle",  Swizzle.make_func(mc.public_variable_list())
       mc.decorate_attr("swizzle", Swizzle.confirm_license_paid)

I call it like this:
@@super_swizzle
class Hourly_Employee(Employee, Contractor):
   IDL_Version = 3.2    # required by metaclass
   Name = ""  # a class attribute...
   SSN = ""
   ....

Unfortunately, this can't unroll gracefully, as it needs some execution
after partially compiling the class, but before it has been
fully finished.  The compilation pseudo-code:

    If there is an @@metaclass:
        remember the metaclass function ("superswizzle")
        attributes will go in a list, not a dict during class compilation
        compile up the class
        create a new Metaclass object with cls, bases, attrs_list
        call @@metaclass function
        take the (changed) Metaclass object, and call 'type' to make it.
        make sure the class remembers which metaclass function to make
on creation.
     else:
        do the usual.

The psudo-code for calling the superclass's @@metaclass is left as an exercise.


So this has the advantages:
+ Metaclass becomes far less boilerplate, error-prone, black magic.
+ Metaclass stops being a subclassing hack, which simplifies things.
+ __metaclass__ goes away.  It was a crufty syntax.
+ the odd syntax of PEP 3115 goes away, while still easy to see if a
    metaclass was used.
+ lots of future hacking on novel ways to use metaclasses
    now that they are easier.
+ Moving the metaclass specialness to a single function simplifies
    diamond inheritence from classes with multiple metaclasses.
- Some people will use metaclasses for stupid things.

This feels like a Python 3000 type of solution.  Still, it is really
late to party.  I would love to see use cases for which this solution
does not work.  Is this a matter of writing a PEP?, writing a sample
cpython patch?, learning to live without?


Charles Merriam

---
Refs:
The PEP
http://www.python.org/dev/peps/pep-3115/

David Mertz's Metaclass Tutorials
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
- A long overview of metaclasses, more than you might want to know.
- Very few uses for metaclasses

Charles Merriam's recent talk
http://charlesmerriam.com/talk


More information about the Python-3000 mailing list