Local variables persist across function calls

Chris Rebert clp2 at rebertia.com
Sat May 15 14:01:03 EDT 2010


On Sat, May 15, 2010 at 10:18 AM, Dave <shepard.dl at gmail.com> wrote:
> I've been writing Python for a few years now, and tonight I ran into
> something that I didn't understand. I'm hoping someone can explain
> this to me. I'm writing a recursive function for generating
> dictionaries with keys that consist of all permutations of a certain
> set. Here's the function:
>
> <code>
> def make_perm(levels, result = {}, key = ''):
>        local = key
>        if levels == 1:
>                for i in ['a', 'b', 'c', 'd']:
>                        result [local + i] = ''
>        else:
>                for i in ['a', 'b', 'c', 'd']:
>                        make_perm(levels - 1, result, local + i)
>        return result
> </code>
>
> The first time I ran it, make_perm(2) does what I expected, returning
> a dictionary of the form {'aa': '', 'ab':'', 'ac':'', ... }. But, if I
> run make_perm(3) after that, I get a dictionary that combines the
> contents of make_perm(2) with the values of make_perm(3), a dictionary
> like {'aaa':'', 'aab':'', ...}. Running make_perm(2) again will return
> the same result as make_perm(3) just did. It's like the local variable
> is preserved across different calls to the same function. I don't
> understand why this happens: "result" is not a global variable, and
> accessing it outside the function generates a NameError, as it should.
> After running make_perm once, printing the value of result inside the
> function produces the last result returned on the first iteration.
>
> If, however, I explicitly pass an empty dictionary into make_perm as
> the second argument, the value of "result" is returned correctly. So I
> have a solution to my problem, but I still don't understand why it
> works. I've tried this on both Python 2.6 and Python 3.2, both in IDLE
> and from the command line, and it behaves the same way. So it seems
> like this is an intentional language feature, but I'm not sure exactly
> why it works this way, or what's going on. Anyway, I'd appreciate it
> if someone could explain this to me.

Default argument values are only evaluated *once*, at
function-definition time, *not* every time the function is called. So
whenever you call make_perm() without specifying the `result`
argument, `result` will get *the same dictionary* as its value every
time, and modifications to that dictionary will thus persist across
calls.

When a default argument value is of a mutable type such as a
dictionary, the following idiom is used to get around the
aforementioned behavior:

def make_perm(levels, result = None, key = ''):
    if result is None:
        result = {}
    #rest same as before...

This ensures `result` gets a fresh dictionary every time.

See also the "Important warning" on
http://docs.python.org/tutorial/controlflow.html#default-argument-values

Cheers,
Chris
--
I'm not a fan of this behavior either.
http://blog.rebertia.com



More information about the Python-list mailing list