Local variables persist across function calls

D

Dave

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.
 
A

Alf P. Steinbach

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>

I bet this is a FAQ, but I don't know where the FAQ is (probably at python.org?).

The defaults for formal parameters are evaluated /once/, namely at function
definition time, when the execution first passes through the definition.

And what you're doing is to update that original default 'result' dictionary.

To achieve the effect that you apparently want you can do


def make_perm( levels, result = None, key = '' )
if result is None: result = {} # Evaluated for each call.
# Blah blah, the rest


There are also other ways.


Cheers & hth.,

- Alf
 
C

Chris Rebert

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top