Advertisement

Python closures, me no understand

Started by February 16, 2012 01:58 AM
5 comments, last by jwezorek 12 years, 10 months ago
In the following code:

def count_if( func, seq):
    count = [0]
    def count_func( x ):
        if ( func(x) ):
            count[0] += 1
    for x in seq:
        count_func(x)
    return count[0]
why do I have to make count a one element list? (obviously I could implement this without a closure I'm just trying to understand how they work.)
Are you sure you have to? I know next to nothing about python, but closures in other languages don't make that requirement.
Advertisement
This
[source lang="python"]
def count_if( func, seq):
count = 0
def count_func( x ):
if ( func(x) ):
count += 1
for x in seq:
count_func(x)
return count
[/source]
yields the error "UnboundLocalError: local variable 'count' referenced before assignment." When I google that I see people talking about this sort of thing but I haven't seen a clear explanation.
I barely know Python, but this StackOverflow thread seems to describe the problem well enough to me. Basically: if you assign to a variable, that variable is created in the local scope, even if there is an identifier with the same name visible from an enclosing scope. Consequently, if you just read from a captured variable, things are dandy. But if you perform an assignment, even after the read, then it will assume that all references in that (enclosed) scope refer to a closure local. Apparently this is remedied (not very nicely, it has to be said) in Python 3.x.
[TheUnbeliever]

I barely know Python, but this StackOverflow thread seems to describe the problem well enough to me. Basically: if you assign to a variable, that variable is created in the local scope, even if there is an identifier with the same name visible from an enclosing scope. Consequently, if you just read from a captured variable, things are dandy. But if you perform an assignment, even after the read, then it will assume that all references in that (enclosed) scope refer to a closure local. Apparently this is remedied (not very nicely, it has to be said) in Python 3.x.

But then why does the list version work? ... Oh, I guess I see, because count[0] += 1 does not tell python to create a one element list if count doesn't exist; if count doesn't exist count[0] += 1 is an error. if a list version of count exists *only* in an enclosing scope and you attempt this assignment python assumes you mean that one?
You can't assign to 'count' (and have it refer to the same one) but you can modify the list it identifies. So, whilst you can't assign a new list to (the enclosing scope's) 'count', you can increment the first value in the list. You want to distinguish between the location 'count' and the value stored there.
[TheUnbeliever]
Advertisement
Oh, okay, so if 'count' was any kind of mutable type the enclosed scope can mutate it ... so the enclosed scope basically has access to count's value but doesn't have a reference to count itself.

This topic is closed to new replies.

Advertisement