[Python-Dev] Multiple dicts for string interpolation?

Ka-Ping Yee ping@lfw.org
Wed, 26 Jan 2000 23:06:01 -0800 (PST)


On Tue, 25 Jan 2000, Skip Montanaro wrote:
> 
>     Guido> I think it depends on to what extent this is a common, useful
>     Guido> idiom.  Do you have evidence of that?  Examples?
> 
> Well, the first place I ran into it was in DocumentTemplates a few years
> ago.  They used an idiom heavily which may have now been replaced by
> acquisition where you'd effectively push and pop value dicts onto a stack as
> you entered and exited nested blocks of DTML code.  Their solution was a
> special dict-like object.

That's really interesting.  I wrote a bunch of Python wrappers for
a UI toolkit a little while ago, and there were two main pieces of
machinery i built to make the toolkit pleasant to use:

    1. a reasonably nice C++ extension class kit (this is where i
       tried overloading operator new for the first time, so it
       would allocate memory that could be freed by PyMem_DEL --
       i don't know if CXX uses the same approach)

    2. a second layer of Python wrapper classes for the extension
       classes that implements extra methods in Python, and maintains
       a hierarchy of default keyword argument values along the
       inheritance hierarchy of widgets

The second of these things involved implementing exactly the kind
of dictionary stack that you mentioned.

The programming idiom for building widgets goes something like:

    defaultstack = {}  # maps class object to list of defaults dictionaries

    class Label(Component):
        defaults = _dict(text="Label", align=LEFT)

    class Button(Label):
        defaults = _dict(text="Button", align=CENTER, shadow=2)

    ...

    w = Window(...)

    Button.push(fg="white", bg="red",
                font="-*-courier-bold-r-normal--14-*-*-*-*-*-*-*")
    a = Button(w, text="one")
    b = Button(w, text="two")
    c = Button(w, text="three")
    Button.pop()

This way you can install some options for a while and make a bunch
of widgets with your defaults, then pop the options and go back to
the previous state.

The layers of dictionaries that get composed in every widget's
__init__ function are (in order of priority):

    1. any non-None keyword arguments to __init__
    2. any non-None values in class.defaults
    3. any non-None values in class.__bases__[0].defaults
    4. any non-None values in class.__bases__[0].__bases__[0].defaults
    etc.

When a new set of defaults is push()ed, the class's current
defaults are saved on the defaultstack for the class, and restored
when pop() gets called.

I don't *know* if this is really the best way to do things, but it
has seemed to work out pretty well in practice.  It makes it more
convenient to deal with all the options you have to throw around
especially when doing UI stuff.

Anyway, i noticed the same sort of thing today while wondering about
using keyword arguments for HTML tag attributes.  (Perhaps HTMLgen
does this sort of thing already -- sorry i haven't looked at it.)
Anyway, the following sort of helper might be useful in general:

    class Default:
        def __init__(self, cl, **defaults):
            self.cl = cl
            self.defaults = defaults

        def __call__(self, *args, **kw):
            for key, value in self.defaults:
                if not kw.has_key(key): kw[key] = value
            return apply(self.cl, args, kw)

Then you could do the same thing as above with:

    MyButton = Default(Button, fg="white", bg="red",
                       font="-*-courier-bold-r-normal--14-*-*-*-*-*-*-*")
    a = MyButton(w, text="one")
    b = MyButton(w, text="two")
    c = MyButton(w, text="three")

This is probably a cleaner way to do things.  I haven't tried it,
but it might be a nice thing to have in Tkinter.
 


-- ?!ng