Scoped change of a global variable

Alex Martelli aleax at aleax.it
Wed Jan 2 06:27:09 EST 2002


"Luigi Ballabio" <ballabio at mac.com> wrote in message
news:mailman.1009966443.13747.python-list at python.org...
    ...
> precision = 2
>
> def add(x,y):
>      sum = x+y
>      tens = math.pow(10.0,precision)
>      return math.floor(sum*tens+0.5)/tens
>
> Now let's say that I want to call add with a different precision but I
> politely want to restore the previous value after I'm done. What I do is:
>
> oldPrecision = precision    # store old value
> precision = 3               # change current value
> x = add(1.0,0.3456)         # do the deed
> precision = oldPrecision    # restore old value
>
> The question is: how can I encapsulate the logic in the comments above?

For example, if the function whose globals you want to tweak (here, 'add')
takes no keyword arguments, a nice interface might be:

def changingGlobals(func, *args, **kwds):
    save = func.func_globals.copy()
    func.func_globals.update(kwds)
    try: result = func(*args)
    finally: func.func_globals.update(save)
    return result

Note you do need the try/finally, because you want to ensure that the
original globals of the function are restored even if the function
should raise an exception (I see that's missing in your snippet).

Another issue with your snippet is that it does not work in any module
except the module where function add was defined.  The changingGlobals
function has no such limitation, since it makes the function itself
tell it which 'globals' dictionary is to be tweaked/restored.

Now,
    x = changingGlobals(add, 1.0, 0.3456, precision=3)
does what you desire.

Of course, it's easy to write a less-general version of changingGlobals
specialized to change a hardwired function-global name rather than whatever
set is specified in the call, etc, etc.


> However, the problem is that even the simple assignment above requires one
> to define a function since it can't be done inside a lambda. Thus, the

Yes, the natural unit for passing code as arguments in Python is
the function.  You just put the assignment _outside_ changingGlobals,
as shown above.  Have all functions communicate through the arguments
they receive and the results they return: this is by far the most
Pythonic arrangement of things.

> P.S. the name of the game here is encapsulating the
> store-set-calculate-restore logic; it is not to show me that there are
> better ways to implement the above than to access a global variable. I
> know. The above is just a contrived 10 lines example to show my point.

...and that point IS...?  The 'set' part is best seen as a function
return in Python, rather than binding some arbitrary name (forcing
it to be global [to what module?!]) -- just as the variable parameter
would be better framed as a function-argument rather than as a
function-global.

Passing a generic piece of code that is not a function (basically
using arguments and return values) is not a normal Python arrangement.
When "environment" dependencies and modifications are desired, that
is often best arranged by building an object (and accessing all the
environment parts as self.something, of course); customization is
then often most easily done by subclassing.

In Python, like in every other language, you'll generally get better
mileage if you use the language as it was designed to be used, rather
than throwing your energies at mimicking the way some OTHER language
was designed to be used.


Alex






More information about the Python-list mailing list