Update locals()

holger krekel pyth at devel.trillke.net
Sun Apr 28 16:57:57 EDT 2002


On Sun, Apr 28, 2002 at 07:16:45PM +0200, Alex Martelli wrote:
> 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.

Are you sure that this holds?

the documentation from python.org says
"""
  globals() 
  Return a dictionary representing the current global symbol table. This
  is always the dictionary of the current module (inside a function or
  method, this is the module where it is defined, not the module from
  which it is called). 
"""

"""
  locals() 
  Return a dictionary representing the current local symbol table.
  Warning: The contents of this dictionary should not be modified; changes
  may not affect the values of local variables used by the interpreter.
"""

So globals() has no such warning (which you stated) and even the warning
in locals() doesn't sound like python is going to *systematically*
prohibit such alterations. It indicates to me that there are cases (which 
we also had yesterday) where the alteration may not affect the calling 
scope. But anyway we certainly agree that "exec" without optional arguments 
*does* execute in current scopes (no matter what behaviour the objects
returned by locals()/globals() have). We are only arguing whether 
there is some good use for execing in current scopes.

> 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. 

Hmmm. certainly a possible update-path. 
I really like about python that you can refer to classes, 
object's attributes/methods, the global/local bindings 
as objects and work with it. This is (one of) the goal
of e.g. "reflective middleware" or generally software which can
introspect and modify its behaviour. It's a very powerful idiom
that many other current languages are missing. Nobody says that you don't
have to care about any side effects this may and will have.

> 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)

Thanks for this transformation. But of course a 'but' follows :-)
As soon as 

- you want to adapt the definition to a filter's method signature 

- have templated or parametrized code in the function body

- want to see what actually gets defined 
  (with my version i can easily write or print the
   actual function's definition and look at a real definition)

it might get difficult or impossible to do it your way. 
Writing a reusable helper function i could say something like

    exec make_adapters(filters, statefulList, '''
    def %name (self, %args):
        """%__doc__"""
        return %sourcescope.%name (%args) (self)
    ''')

or even better make the templated adapter definition a defaulted 
argument. This provides more flexibility for the *caller* of the helper
function without ever having to modify the helper function.

Also i use the template 'exec in current scope' method to allow 

resultfilters = "isfile and nolink and fnmatch('data*')"

to be compiled at run time into a function and then be
applied to - say - 15000 files. This technique is similar 
to what the regular expressions module does with re.compile. 
This may be a better example than the original one?!
At least i don't see how you can apply your techniques 
to it without using slow 'evals' or a slow interpretation
layer.

> 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)

None.strip() in 

...__dict__.get(name).__doc__.strip() 

would fail at run time if a filters's doc has not been defined.
Same with your code.

> 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...

That's very interesting and i will investigate this further.
But my above points can certainly be raised here, too.

> > - 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.

OK. 

1. My quest against redundancy is certainly not over :-)

2. It's obvious that i could turn the given example into
   something reusable as well (and it would honour the actual
   arguments via inspect.getargspec among other nicities).
   I just didn't want to blow the example to inifity
   and provide some direct context.

I appreciate your explanations and code very much. 

The only better thing i can imagine is if you tried a tiny bit 
harder to see some *valid* points rather than only pin down invalid points.
Otherwise you give me the feeling that i am completly on the wrong path.
(or is this intended?!)

I just try to communicate a use for "exec" where it is preferrable
to not use explicit dictionaries. 

regards,

    holger





More information about the Python-list mailing list