On Sat, May 15, 2010 at 10:18 AM, Dave <shepard...@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 -- http://mail.python.org/mailman/listinfo/python-list