On 02/01/13 11:01, Oleg Broytman wrote:
On Tue, Jan 01, 2013 at 03:17:34PM -0800, alex23
wrote: On Jan 2, 8:16 am, Chris Angelico
wrote: It's a little odd what you can and can't do, until you understand the underlying system fairly well. It's something that's highly unlikely to change; one of the premises would have to be sacrificed (or at least modified) to achieve it.
By this definition, though, every feature of Python that someone doesn't understand is a wart. For a new user, mutable default parameters is a wart, but once you understand Python's execution& object models, it's just the way the language is.
Generally, I find "wart" means "something the user doesn't like about the language even if it makes internal sense".
What about warts that don't have internal sense? Mutable default parameters are just artifacts of the implementation. What is their "internal sense"?
They are not artifacts of the implementation, they are a consequence of a deliberate design choice of Python. Default values in function definitions are set *once*, when the function object is created. Only the function body is run every time the function is called, not the function definition. So whether you do this: def ham(x=0): x += 1 return x or this: def spam(x=[]): x.append(1) return x the default value for both functions is a single object created once and reused every time you call the function. The consequences of this may be too subtle for beginners to predict, and that even experienced coders sometimes forget makes it a wart, but it makes perfect internal sense: * in Python, bindings ALWAYS occur when the code is executed; * in Python, "x=<whatever>" is a binding; * even inside a function definition; * def is a statement which is executed at run time, not something performed at compile time; * therefore, inside the statement "def spam(x=[]): ..." the binding x=[] occurs ONCE ONLY. The same list object is always used for the default value, not a different one each time. Early binding of function defaults should, in my opinion, be preferred over late binding because: * given early binding, it is clean to get late binding semantics with just one extra line. Everything you need remains encapsulated inside the function: def spam(x=None): if x is None: x = [] x.append(1) return x * given late binding, it is ugly to get early binding semantics, since it requires you to create a separate global "constant" for every argument needing an early binding: _SPAM_DEFAULT_ARG = [] # Don't touch this! def spam(x=None): if x is None: x = _SPAM_DEFAULT_ARG x.append(1) return x -- Steven