TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

Python's Mutable Default Problem

68 pointsby tswicegoodover 14 years ago

17 comments

sophaclesover 14 years ago
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.
评论 #2242291 未加载
评论 #2242259 未加载
评论 #2242255 未加载
astonover 14 years ago
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.
评论 #2242379 未加载
评论 #2243258 未加载
评论 #2242471 未加载
评论 #2242799 未加载
评论 #2244155 未加载
perlgeekover 14 years ago
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(&#38;integrand, $from, $to, $step = ($to - $from) / 100) { ... }</code></pre>
emehrkayover 14 years ago
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.
riobardover 14 years ago
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.
spenroseover 14 years ago
Yes, it's a wart. You learn the patterns he mentions pretty quickly.
评论 #2242911 未加载
neutronicusover 14 years ago
"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).
评论 #2242306 未加载
评论 #2242710 未加载
评论 #2242909 未加载
mark-rover 14 years ago
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?
评论 #2243989 未加载
dustingramover 14 years ago
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())&#62;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>
评论 #2243912 未加载
billmcnealeover 14 years ago
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).
wyuenhoover 14 years ago
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>
pedro3005over 14 years ago
You can use this trick for memoization. Example: <a href="http://paste.pocoo.org/show/341849/" rel="nofollow">http://paste.pocoo.org/show/341849/</a>
评论 #2242914 未加载
评论 #2243969 未加载
clayover 14 years ago
"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
viraptorover 14 years ago
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.
baqover 14 years ago
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.
jfm3over 14 years ago
The Pythonist doth protest too much, methinks.
drstrangevibesover 14 years ago
i think its clearer and more pythonic in this case to do<p>def function(item, stuff): .... blah blah def function(item): function(item, [])