Default scope of variables

Steven D'Aprano steve+comp.lang.python at
Sun Jul 7 18:03:51 CEST 2013

On Sun, 07 Jul 2013 23:43:24 +1000, Chris Angelico wrote:

> On Sun, Jul 7, 2013 at 11:13 PM, Wayne Werner <wayne at>
> wrote:
>> Which you would then use like:
>> conn = create_conn()
>> with new_transaction(conn) as tran:
>>      rows_affected = do_query_stuff(tran)
>>      if rows_affected == 42:
>>           tran.commit()
> Yep. There's a problem, though, when you bring in subtransactions. The
> logic wants to be like this:
[snip hideous code]
> I don't like the look of this. It might work, but it's hardly ideal.
> This is why I like to be able to nest usages of the same name.

Yes, and the obvious way to nest usages of the same name is to use a 
instance with a class attribute and instance attribute of the same name:

class Example:
    attr = 23

x = Example()
x.attr = 42
del x.attr

If you need more than two levels, you probably ought to re-design your 
code to be less confusing, otherwise you may be able to use ChainMap to 
emulate any number of nested scopes.

One interesting trick is you can use a ChainMap as function globals. 
Here's a sketch for what you can do in Python 3.3:

from types import FunctionType
from collections import ChainMap

class _ChainedDict(ChainMap, dict):
    # Function dicts must be instances of dict :-(

def chained_function(func, *dicts):
    """Return a new function, copied from func, using a 
    ChainMap as dict.
    dicts = dicts + (func.__globals__, builtins.__dict__)
    d = _ChainedDict(*dicts)
    name = func.__name__
    newfunc = FunctionType(
            func.__code__, d, name, closure=func.__closure__)
    newfunc.__defaults__ = func.__defaults__
    return newfunc

And in use:

py> f = chained_function(lambda x: x+y, {'y': 100})
py> f(1)
py> f.__globals__.maps.insert(0, {'y': 200})
py> f(1)
py> del f.__globals__.maps[0]['y']
py> f(1)


More information about the Python-list mailing list