Problem with Lexical Scope

Duncan Booth duncan.booth at invalid.invalid
Mon Dec 12 04:00:14 EST 2005


jslowery at gmail.com wrote:

> I am using python2.4 and the following code throws a "status variable"
> not found in the inner-most function, even when I try to "global" it.
> 
> def collect(fields, reducer):
>     def rule(record):
>         status = True
>         def _(x, y):
>             cstat = reducer(x, y)
>             if status and not cstat:
>                 status = False
>             return y
>         return reduce(_, [record[field] for field in fields])
>     return rule
> 
> What gives?

You rebind status in the inner function. Binding to a name makes it local 
to that function (unless declared global). A name in an enclosing function 
is neither local nor global so you cannot rebind it.

Ways round this include storing the status in a mutable value, making it an 
attribute of a class, or rewriting the code to not require setting an outer 
variable in the first place.

The last of these is the easiest in this case: status is never actually 
used or returned anywhere so you could just remove all references to it 
with no impact on the code. In fact, so long as reducer has no side 
effects, you could just do this:

def collect(fields, reducer):
     def rule(record):
         if fields:
             return record[fields[-1]]
     return rule

Alternatively, if I assume you actually wanted rule to return the status as 
well as y, then the outer assignment disappears quite easily:

def collect(fields, reducer):
     def rule(record):
         def _((x, status), y):
             cstat = reducer(x, y)
             return (y, status and cstat)
         return reduce(_, [record[field] for field in fields], (0, True))
     return rule

If reducer has no side effects this can be further reduced:

def collect(fields, reducer):
     def rule(record):
         def _((x, status), y):
             return (y, status and reducer(x,y))
         return reduce(_, [record[field] for field in fields], (0, True))
     return rule

Given that the final y returned is simply the last record[field] maybe you 
only wanted the status value? In that case expanding the reduce and _ into 
inline code is likely to make things clearer:

def collect(fields, reducer):
     def rule(record):
        prev = 0
        for field in fields:
            if not reducer(prev, record[field]):
                return False
            prev = record[field]
        return True
     return rule



More information about the Python-list mailing list