I'm not sure this constitutes any problem other than a lack of understanding of the python runtime. What the author describes as:<p><i>"the mutable default parameter quirk is an ugly corner worth avoiding"</i><p>could also be described as:<p><i>"a natural outcropping of python's late binding, "names are references" variable model, and closure mechanisms, which provide a consistency to the language that is often crufted up in others"</i><p>I do somewhat agree with the author that this particular functionality should be a "use only when needed" feature. I don't think it should be avoided at all costs tho, because there are times where the mutable default allows for a lot of saved code. In fact in a few cases the code to work around using mutable defaults can get into some serious voodoo because frequently the writer is actually trying to work around the bigger mutable/immutable objects and names are references "issues" in python.<p>This also reminds me of something I was reading on the front page today about the old 'use the whole language' vs 'simplicity is king' holy war.
As noted in the comments, DON'T use<p><pre><code> stuff = stuff or []
</code></pre>
because if you pass an empty list, you'll get a new one rather than mutating the one you passed.<p><pre><code> stuff = stuff if stuff is not None else []
</code></pre>
is wordy, but at least it's correct.
FWIW, Perl 6 implicitly treats default values as closures, and calls them when no argument is passed that could bind to the optional argument.<p>That way you get a fresh array each time, and you can even use defaults that depend on previous arguments:<p><pre><code> sub integrate(&integrand, $from, $to, $step = ($to - $from) / 100) { ... }</code></pre>
Pylint (or maybe it was pep8) has told me not to make dicts default arguments when running it against my code, but didn't explain why. Thanks for the post.
You just need to fully understand when Python does evaluation.<p><pre><code> import types
def function(item, stuff = lambda: []):
if type(stuff) == types.FunctionType:
stuff = stuff()
stuff.append(item)
print stuff
function(1)
# prints '[1]'
function(2)
# prints '[2]'
</code></pre>
In Scala it has a better syntax because of typed function object:<p><pre><code> trait Map[A, B] {
…
def getOrElse (key: A, default: ⇒ B): B
…
}
</code></pre>
the `default` parameter is a function, so when you do<p><pre><code> getOrElse(someKey, defaultValue)
</code></pre>
The `defaultValue` becomes a function that generates the value you put there when called.
"stuff = stuff or []"<p>This idiom is baked into the perl community. It's kind of funny to see a Pythonista deciding it's a good idea. (You can tell it hurts him too).
I keep seeing this "problem" come up, but I don't understand how it's realistic. If you have a function that modifies a parameter as a side effect, why would you have a default value for the parameter?<p>And since the site's comments seem to be taken over by link spam, is this mention on Hacker News just a clever way to juice the Google rank of said spam?
This 'problem' can actually come in handy when used with a regex callback function.<p>See if you can determine what this does:<p><pre><code> def cbk(match, nb = [0] ):
if len(match.group())==len(nb):
nb[-1] += 1
elif len(match.group())>len(nb):
nb.append(1)
else:
nb[:] = nb[0:len(match.group())]
nb[-1] += 1
return match.group()+' '+('.'.join(map(str,nb)))
str = re.compile('^(#+)',re.MULTILINE).sub(cbk,str)</code></pre>
The problem is not so much about mutable, it's that default parameters escape the scope of their method.<p>Which is very, very messed up (but not the first thing Python messed up).
It's on SO's Python FAQ too. This is the only thing in Python that's really bitten me. I remember it took me like a week to figure this out when I was tearing down my algorithm bit by bit to find out whether my prove was wrong or the code.<p><a href="http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument" rel="nofollow">http://stackoverflow.com/questions/1132941/least-astonishmen...</a>
"stuff = stuff or []"<p>This would fail to have expected behaviour here:<p>fill_list = []<p>stuff = function(info, full_list)<p>print fill_list<p>use the if list is None: paradigm
Not sure if that's a "problem" if the only other way to implement function-static variables would be to add a variable visible for the whole module, or tricks with decorators...<p><pre><code> @statics(blah=[])
def foo(normal_args, **kwargs):
# or
def foo(normal_args, blah):
</code></pre>
It messes up the idea of looking at the definition to find the function signature.
it's a gotcha but it makes perfect sense once you understand why it works that way - i.e. the difference between evaluating a function definition and calling it.