metaclass and customization with parameters

Alex Martelli aleaxit at yahoo.com
Sun Oct 3 18:35:02 EDT 2004


zipher <zondervanz at gmail.com> wrote:

> aleaxit at yahoo.com (Alex Martelli) wrote in message
news:<1gl2s8a.1imgbyqcxntogN%aleaxit at yahoo.com>...
> > > 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.
> 
> Yes, I should have put that in quotes.  What I meant to convey was
> that I want to be able to create a class 'on the fly' without having
> to define it first.

That's exactly what a class statement does: it creates a class on the
fly.  There is no concept of "having to define it first" in Python; you
may be thinking of other and very different languages.

> > > 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    
> 
> Yes, your suggestions were my second, and first implemenations
> respectively.  However, both require defining a class by the 'user',
> so to speak, as opposed to letting the user simply call a 'function'
> that returns the customized class with the filter class attribute
> already set.

All they require is using the statement that is normally used to create
classes, namely 'class', rather than a statement which is rarely used,
and less powerful and convenient, for the same purpose, namely an
assignment statement.  The "filter attribute already set" issue appears
to me to be a red herring: why should it make any difference for the
user if the code string
    filter=int
appears within parentheses (in the call to Metadict that you want) or
without them (in the 'class' statement that appears to me to be the one
obvious way to accomplish the identical purpose)?


> Another reason it would be helpful to be able to wrap this in an
> expression like "Metadict(filter=int)" is because I can then use this
> as a parameter passed to other parts of the application.  (Although,
> as you suggested, I'd have to pass the name as well.)

Yep, if you want to supply a name you have to code it SOMEwhere, and why
shouldn't one code it in the normal way, right after the 'class'
keyword?


> For example, in keeping with the aforementioned example, consider
> creating a basic integer-weighted Graph class starting with the
> following:
> 
> #A Graph is a dict whose values are Vertex.
> #A Vertex is a dict whose values are int.
> >>> Graph = Metadict(name='Graph', filter=Metadict(name='Vertex',
> filter=int))

versus:

class Graph(filtered_dict):
    class Vertex(filtered_dict):
        filter = int
    filter = Vertex

all the rest that you write in the following:

> >>> g = Graph()
> # create outgoing links on graph vertex 1 to 3,4,5:
> >>> g[1] = {3: 1, 4: 2.0, 5: '3'}
> #this raw dict above will be converted to Vertex because 
> # of the Graph.filter function and assigned to g[1]
> #Vertex.filter automatically coerced dict values to ints:
> >>> g[1]              
> Vertex({3: 1, 4: 2, 5: 3})

...just happens the same way, it makes no difference that you've used
the normal and usual way (the class statement) rather than the strange
way you want to define classes Graph and Vertex.


> But perhaps a simpler way to illustrate my intent is to just imagine a
> series of dictionary classes which restrict all their values to a
> certain type (through the filter function) before insertion into the
> dictionary.  Dictionaries of ints, lists, and strings along with
> custom filters that, say, validate the values for correct spelling, or
> whatever.
> 
> Now you ("I" ;^) don't want to require the user to create a new class
> for every possible filtered dict variety.

I could care less.  You asked for some way to do exactly this: have the
user create a new class object for every filtered dict variety the user
needs.  I'm not getting into whether this is a good or bad idea.  I'm
just saying: the normal way to have the user create new class objects is
by having the user execute 'class' statements; I see no advantage to
your preference of having the user execute function calls instead.

>  And, yes, you could avoid
> this by just subclassing dict and passing the filter function to
> __init__ and setting it as an instance attribute, but then you have an
> extra attribute stored for every instance of your filtered dictionary
> and the (albeit) small cost of assigning this attribute for every
> instance created.  (Consider again the Graph example, where there are
> going to be many instances of the Vertex class within the Graph and
> you don't want a unique filter function for each Vertex instance
> within the Graph.)

As I said, I'm not examining _at all_ the issue of making new classes
for each value of filter, vs storing such values in instances instead,
etc.  _ALL_ I'm addressing is: if you want to make new classes, the
normal way is the 'class' statement, rather than a function call, and I
don't see why you insist on the class object creation having to happen
via a function call instead.
 
> This would seem like an ideal application for a metaclass (in the
> abstract sense, not necessarily in the way Python implements the
> concept):  I want something that will create and return a specialized
> class based on the parameters I pass to it.

You have it: it's the 'class' statement.  The only metaclass in play is
'type' (the normal metaclass of all new-style classes), of course.

Still, having hopefully communicated that your syntax preference for
function calls rather than perfectly good 'class' statements is really
very troublesome to me, and that I believe your software will be much
better if you let the user use 'class' statements to create classes
unless you have strong reasons to do otherwise (and I really don't see
what reason you have in this case), here's a couple of alternatives:

def but_WHY_ever(name, filter):
    class anon(filtered_dict): filter=filter
    anon.__name__ = name
    return anon

or

def I_really_dont_see_WHY(name, filter):
    return type(name, (filtered_dict,), dict(filter=filter))

so, now the following three constructs have become equivalent:

class Graph(filtered_dict): filter=int

Graph = but_WHY_ever('Graph', filter=int)

Graph = I_really_dont_see_WHY('Graph', filter=int)

to me, the first one seems by far the best.  But if you want to confuse
the issues by using the others, go right ahead: never be it said that
Python doesn't offer you enough rope to shoot yourself in the foot!-)


Alex



More information about the Python-list mailing list