metaclass and customization with parameters

Alex Martelli aleaxit at yahoo.com
Sun Oct 3 04:27:15 EDT 2004


zipher <zondervanz at gmail.com> wrote:

> After searching through comp.lang.python and the web regarding
> metaclasses, I could not find an example for customing classes using
> metaclass parameters.

When a class statement executes, Python determines the metaclass (e.g.
by finding a '__metaclass__' attribute in the class body) and then calls
it with three parameters -- never more, never fewer:

class name(bases):
    __metaclass__ = metaboo
    ...rest of class body snipped...

is exactly equivalent to:

name = metaboo('name', bases, d)

where 'd' is the dict that remains after execution of the class body
(all barenames bound in the body -- be that by def, class, assignment,
import, whatever -- are left as entries in that 'd').

> I want to be able to create a class at runtime by calling some

All classes are created at runtime, normally by executing (at runtime:
there is no other 'time'...;-) a 'class' statement.

> function or 'meta-constructor' which returns a customized class and
> sets a class attribute according a given parameter.
> 
> Ideally, I'd be able to do something like:
> 
> >>> Bag = Metadict(filter=int) #Metadict returns a new class named
> 'Bag'

The normal Python syntax for that purpose might rather be:

class Bag:
    __metaclass__ = Metadict
    filter = int

and for that use, Medadict would be rather simple to write... but
your stated purpose (to this point) is even better accomplished by:

class Bag(dict):
    filter = int    

> >>> issubclass(Bag, dict)      #  which is a type of dict
> True
> >>> Bag.filter                 #  with class attribute set accordingly
> <type 'int'>
> >>> b = Bag({'pennies': 6.0, 'dimes': 3})  #create instances of this
> new class
> 
> (Aside:  the issue of magically knowing the variable name (i.e. 'Bag')
> in that first assignment statement and setting the class name
> accordingly would be ideal, but I could live with passing the name as
> a parameter to Metadict if necessary.)

If you want to explicitly call Metadict as a factory, rather than use a
'class' statement, yep, there is then no way in which Metadict can know
what name its result will be bound to, so you'd have to pass it.  But
why isn't a class statement even better?

> There would also be a number of new and re-defined methods which would
> be part of all classes constructed and returned by calls to Metadict.
> For example, somewhere I need to re-define dict's __setitem__ method
> to something like:
> 
> >>> def __setitem__(self, key, value):
> ...     self[key] = self.filter(value) #coerce/validate values before
> adding to dict
> 
> Ideally, all such methods would be in their own class definition since
> they will be common to all classes created by Metadict (perhaps they
> would need be defined within Metadict itself).

No, this is best done by inheritance:

class Bag(filtered_dict):
    filter = int

with, e.g:

class filtered_dict(dict):
    def __setitem__(self, key, value):
        dict.__setitem__(self, key, self.filter(value))

> This seems like a simple thing to want, but unusally convoluted to
> actually implement.  I'm not even sure where to start.
> 
> Any pointers?

I would start with inheritance, because I don't see what value a custom
metaclass would add; indeed I see only downsides, such as needing to
pass the intended class name, if you want to use explicit-call syntax.

If it does turn out that subclasses of filtered_dict all DO need a
custom metaclass, for reasons connected to specs you haven't yet
mentioned, just add the metaclass (with a __metaclass__ in the body) to
filtered_dict, and every subclass thereof will inherit it.  That's all
there is to it... class statement, and inheritance, appear to give you
all that you have required/explained so far, except the kind of syntax
you deem ideal (but is really second best, due to the need to pass a
class name...).


Alex



More information about the Python-list mailing list