Update locals()

Alex Martelli aleax at aleax.it
Sun Apr 28 13:16:45 EDT 2002


On Sunday 28 April 2002 01:52 pm, holger krekel wrote:
	...
> > Please give ONE example which, in your opinion, is best handled
> > with 'exec' without explicit dictionaries rather than in any other way.
>
> i try to make it short and answer questions (if any of you gurus
> ever have any :-) as needed. Obviously you can always pass explicit
> dictionaries to exec (globals, locals). That can't be the point.

Nope.  locals() and globals() are allowed by the language definition to be 
mappings rather than fully general dictionaries, and in particular any
alteration performed on locals() or globals() is allowed (this could change
at any time, even in a micro-version or a bug-fix one -- the docs have been 
saying so for a LONG time) to [a] alter the namespace, [b] silently
ignore the alteration attempt, [c] raise an appropriate exception.

In the current implementation, locals() alterations when the namespace is in 
a function follow rule [b] (they're silently ignored), other locals() and 
globals() alterations rule [a], but no program that relies on either behavior 
is correct Python (might be nice for PyChecker to warn, but it's probably too 
hard to track).  I _think_ the best solution might be to have them as 
mappings that give a warning when altered, on the way to making them 
read-only per rule [c] in a release or two. 

> I cite some of my code.
>
> class filename:
>     """ Abstraction for filename ... """
>     # ...
>
>     for name in filters._stateful_all:
>         doc=filters.__dict__.get(name,undoc).__doc__.strip()+'\n'
>         exec indentedcode("""
>
>     def %(name)s (self,*args,**kargs):
>         '''%(doc)s'''
>         return filters.%(name)s (*args,**kargs) (self)
>     """ % locals())

Basically you want to add to class filename a bunch of methods, each
delegating as you show to the homonym callable attribute of global object 
filters.  This is most easily done after the filename class object is
complete and available - i.e. right after the class statement, or in a
metaclass.  The "right after" approach is probably easier (using Python
2.2 for simplicity, doesn't make much difference anyway):

def addMethods(class_, filters, statefulList):
    for name in statefulList:
        delegatee = getattr(filters, name)
        def method(self, *a, **k): return delegatee(*a, **k)(self)
        method.__doc__ = getattr(filters, name, undoc).__doc__.strip() + '\n'
        setattr(class_, name, method)

You may need to tweak this (I'm not sure why the filter.name access is
unconditional when looking for the delegatee, but falls back to undoc when
looking for the __doc__, for example).  But the point is that this way the
code is reusable (you may apply it to as many separate classes as you
wish, several sequences of methodnames for the same class, etc), clean,
efficient -- all claims that don't hold for the exec-based approach.  Most
often, setattr and getattr do offer superior alternatives for such kinds of
metaprogramming.

If having to explicitly call

addMethods(filename, filters, filters._stateful_all)

right after the 'class filename:' statement body's end nags you, you can
use a custom metaclass for the same purpose, e.g.:

class filename(methods_adder):
    _methods_source = filter
    _methods_names = filters._stateful_all
    # etc etc

with:

class MC_methods_adder(type):
    def __init__(self, *args):
        class_ = type.__init__(self, *args)
        methods_source = getattr(class_, '_methods_source', None)
        methods_names = getattr(class_, '_methods_names', ())
        addMethods(class_, methods_source, methods_names)
        return class_

class methods_adder:
    __metaclass__ = MC_methods_adder

or the like (untested code, but I hope the general idea is clear).
Custom metaclasses (in 2.2., at least) can be handy for such
metaprogramming jobs.


> - i am a maniac at avoiding redundancies.

...and yet the infrastructure embodied in class filename as you coded
it is not reusable save by copy-and-paste, the worst kind of redundancy.
addMethods and the MC_methods_adder metaclass avoid redundancy
by placing such metacoding-infrastructure in ONE place.


Alex





More information about the Python-list mailing list