14 ms·
It's worth explaining why mutable defaults are bad. The problem with mutable defaults is that they are evaluated once only when the function is defined. Each t
by halayli 14y ago
It's worth explaining why mutable defaults are bad.
The problem with mutable defaults is that they are evaluated once only when the function is defined. Each time the function is called you'll be using the same mutable variable that was created during function definition.
- ollysb 14y agoThat seems like decidedly unexpected behaviour and makes default params far less useful.
- jsprinkles 14y agoActually, if you understand the way Python is evaluated (dig in, the core is pretty transparent), it's the only behavior that makes sense in this case. It's also documented as such[1], so it's quite expected. Default parameters are still just as useful for constants, such as: def f(x=0, y="foo", z=3.14159): This, however, is a perfectly Pythonic idiom: def f(L=None): if L is None: L = [] [1]: http://docs.python.org/reference/compound_stmts.html#function-definitions http://docs.python.org/reference/compound_stmts.html#functio...
- swdunlop 14y agoOr just embrace the tao of the tuple: def f(L=()): ...
- ollysb 14y agoWhile it seems logical when you understand what's going on, from a practical point of view I can't see how this would ever be useful. The tradeoff appears to be that the functions are first class objects. I'm not sure what the benefit here is though. Does having them as first class objects allow some useful idioms? (I'm a ruby dev but I'm genuinely curious to know what this allows you to do)
- jsprinkles 14y agoIt's not designed to be useful, it just is. Functions are objects, yes, and the def statement brings them into being and assigns them to the given name in the current scope: >>> def f(x): ... return x + 5 >>> type(f) <type 'function'> >>> dis.dis(f.func_code) 2 0 LOAD_FAST 0 (x) 3 LOAD_CONST 1 (5) 6 BINARY_ADD 7 RETURN_VALUE >>> g = f >>> g(10) 15 >>> g is f True They're just variables in the current scope. If you're quite clever, your brain is already figuring out that has some interesting implications that some libraries use: >>> import socket >>> socket.gethostbyname('www.google.com') '74.125.71.103' >>> socket.gethostbyname = lambda i: '10.0.0.1' >>> socket.gethostbyname('www.google.com') '10.0.0.1' I will not pass judgement on monkey patching like this, just pointing out it's doable. I know for a fact Ruby can as well. Functions just being variables has useful properties when you're doing something like fancy switch/case type things (the readability of this is questionable, but it's cool to look at it, like a Duff's device): >>> i = 1 >>> { str: func1, unicode: func2, int: func3 }.get(type(i), func1)(i) in func3 Also consider something like this, which is how decorators work (and they're incredibly useful), sort of like a closure: >>> def maker(i): ... def ret(x): ... return x + i ... return ret >>> f, g = maker(10), maker(100) >>> f(5), g(10) (15, 110) I'd be surprised if Ruby couldn't do everything I just did.
- mattmanser 14y agoIt's not designed to be useful By useful he means mucking up your program in totally unexpected ways. He's being nice about it being a silly decision to have it behave that way. The entire post reads more like a list of unexpected things that will bite you in the ass.
- adi92 14y agoThanking you for showing me the 'dis' module. Dis is going to be fun to play with!!
- philwelch 14y agoFunctions (methods) are first class objects also in Ruby: methods are instances of the class Method, while Procs are a lightweight alternative. Default arguments in Ruby are not mutable in any kind of function object, be it a lambda proc, a regular proc, or a method. You can use a mutable default argument as an ersatz static variable, e.g. for memoization.
- chernevik 14y agoI am but an egg, but isn't this the same but shorter? def f(L=None): L = L or []
- coderdude 14y agoIt might work the same depending on your use of it but it's not the same. In that instance L will become a blank list if it is equal to None, zero, or a zero length string. There are many cases where this wouldn't affect anything, but there can also be instances where that will cause you to define L as a blank list when you really wanted to keep L's value. I think it's always better to be explicit and test for the value(s) you expect.
- chernevik 14y agoAh. These are the little assumptions that keep blowing off my feet. Thanks.
- SoftwareMaven 14y agoIt is these cases that brought the ternary operator to Python: def f(x=None): x if x is not None else []
- iki23 14y agoYes, but why? Actually, it's No, because python culture aims to use one way to do thing, the least surprising one. In this case it's: def f(x=None): if x is None: x = []
- SoftwareMaven 14y agoBecause that "if" is a statement whereas the ternary expression is, well, an expression. There are places expressions can be used that statements can't (eg lambdas) and that x or [] won't work (eg when x is False). That said, people still seem to favor your form as the more Pythonic way. Personally, I think that's just because the ternary expression is relatively new.
- Estragon 14y agoIt's like whitespace. Everybody has this reaction at first, then they get over it.
- gbog 14y agoAhem, we have repeatedly intricate issues that are related to this kind of behavior, and I have yet to see an annoying white space bug. Not to say I think python should be changed on this point. It shouldn't, there are code checkers that warn you on the gotcha, let's use them.
- makecheck 14y agoThe thing is, None is always a possible value for a parameter so it's actually more robust if functions are written to expect None. If you say "f(x=[])" (assuming that worked without the actual side effects it has), someone could still say "x(None)" instead of "x()", causing the function to die. Since a robust program isn't able to avoid checking for None, it might as well set defaults there too. There is another case where this is important; you might want the equivalent of "f(x=expensive_function_to_calculate_useful_default())", and you don't want that function called unless it needs to be. Only the x=None approach allows this to be deferred.
- anthonyb 14y agoI disagree. If you want your programs to be robust like that, you now have to check for every case where someone might pass in something stupid (dict instead of int, maybe?). Much better to catch errors further up the chain and keep your low level code simple (ie. pass me something other than an iterable and I blow up). In the expensive case, I'd just calculate it once and store it somewhere (possibly as a lookup dictionary if there are multiple inputs) and access that from within the function.
- makecheck 14y agoWell that's true, a function is generally written as if it's been given what it wants. I wouldn't check for other types either. But None is a result that can happen in situations that would otherwise return exactly the expected type. If "nothingness" can be meaningful (especially in a function that accepts an empty list as a parameter, say), it's nicer if the code just deals with None itself instead of requiring checks for None in all the callers.
- ScottBurson 14y agoIt's a mistake. Default values for optional parameters don't act that way in any other language I know of, including Common Lisp, in which functions are also firstclass.
- jsprinkles 14y agoIt's most certainly not a mistake; Python 3 would probably have fixed it, if it were. It is an (admittedly, strange) side effect of the way 'def' works.
- ScottBurson 14y agoBut 'def' doesn't have to work that way. Consider, in CL: > (defvar *fn* (let ((x 3)) (lambda (&optional (y (list nil x))) (push 7 (car y)) ; modifies the list y))) *FN* > (funcall *fn*) ((7) 3) > (funcall *fn*) ((7) 3) From this example you can see two things. First, the binding of 'x' is closed over when the lambda expression is evaluated. And second, the expression that provides the default value of 'y' is evaluated every time the function is called. There's no fundamental reason it couldn't have worked that way in Python. (I understand that changing the language so it worked that way now would likely break some code.) EDIT: fixed formatting.
- Estragon 14y agoExpensive, though. http://stackoverflow.com/questions/1651154/why-are-default-arguments-evaluated-at-definition-time-in-python http://stackoverflow.com/questions/1651154/why-are-default-a...
- ScottBurson 14y agoSurely not. The implementation already has to check that the number of provided arguments is valid. The decision of whether to evaluate the default expression can be part of that. The code that evaluates the default expression doesn't need to be in a separate function, either, so the argument that calling that function is too expensive also doesn't hold water. I just tried a test in SBCL: (defun foo1 (x) x) (defun test1 (n) (dotimes (i n) (foo1 (cons nil nil)))) (time (test1 100000000)) => 4.4 sec, or 44ns / iteration (defun foo2 (&optional (x (cons nil nil))) x) (defun test2 (n) (dotimes (i n) (foo2))) (time (test2 100000000)) => 4.1 sec, or 41ns / iteration The version with the optional parameter is actually slightly faster, which completely blows a hole in the performance argument. Look, no language is perfect -- not even Common Lisp :-) I think users are better served when design flaws in a language are acknowledged without defensiveness than when bogus justifications are offered.
- Pheter 14y agoI came here looking for an explanation for this, so thanks. To the curious - SO has an explanation of why Python was designed like this, which I found interesting: http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument http://stackoverflow.com/questions/1132941/least-astonishmen...
- xyzzyz 14y agoI don't buy this explanation. Actually, this is not a design flaw, and it is not because of internals, or performance. It comes simply from the fact that functions in Python are first-class objects, and not only a piece of code. Why in Common Lisp defaults behave the way one would expect, then? Functions are also first class, but defaults are evaluated at every call.
- sophacles 14y agoI don't know CL, but in python function definitions can be executed multiple times... at the top level this happens at module import, so it ends up being only once. But in nested definitions, e.g. def foo(): def bar(): ... return bar foo() == foo() # false Two different function objects are created. If, in the above examle, bar took a pram thelist=[], each call to foo would produce a bar function with a different list instance for thelist. The default values can be read as expressions passed to the function object constructor, rather than a bit of code to be evaluated each function run. I don't know how CL works in this regard, nor do I know which is better or worse. I think the explanation linked did a terrible job conflating first class functions with execution and runtime models. Some of the answers below it explain better tho. :)
- xyzzyz 14y agoThe fact that definition can be evaluated many times is not really relevant here. What is important is how one specifies language's semantic -- for instance, Common Lisp: The Language, 2nd edition book (I don't own ANSI standard) says: When the function represented by the lambda expression is applied to arguments, the arguments and parameters are processed in order from left to right. (...) If optional parameters are specified, then each one is processed as follows. If any unprocessed arguments remain, then the parameter variable var is bound to the next remaining arguments, just as for required parameter. If no arguments remain, however, then the initform part of the parameter specifier is evaluated, and the parameter variable is bound to the resulting value (...). The CLTL2 specifies that the form representing the default value of optional parameter shall be evaluated every time the parameter is not provided.
- reinhardt 14y agoThis also explains when mutable defaults are not a problem: when they are not mutated in the function's body. There's nothing wrong with this: def f(seq=[]): for x in seq: # do something
- gbog 14y agoYes but no. In two month, when the function has grown, the next coder may not notice the issue and start mutating the default in the function body. Then you have a hidden killer bug. Pass all code under pylint scrutiny, comply to its complains or adjust its rules, do it early. That is the recommendation I wish all devs could read.
- tzs 14y ago> It's worth explaining why mutable defaults are bad They can also be good. Here's an example from the Reddit discussion, showing how a mutable default can be used to very neatly and cleanly add memorization to a function: def fib(n, m={}): if n not in m: m[n] = 1 if n < 2 else fib(n-1) + fib(n-2) return m[n]
- ZenPsycho 14y agoIs that clean and neat, or weird and inscrutable? Will it be clear to /anyone/ reading that code what it's doing?
- o1iver 14y agoI agree with your sceptisism. This is misusing a language feature, a better approach is using a decorator.
- tzs 14y agoI'm a Python newbie, and that code was immediately clear and obvious to me when I read it. I won't say it would be clear anyone that reads it, because we live in a world where people who claim to be programmers can't do fizz buzz.
- StavrosK 14y agoI'm a Python veteran, and, if you do it like this, I'll shoot you. Add a @memoize decorator and do it there, you need to always be as obvious as possible. Compare: @memoize def fibonacci(n): pass def fibonacci(n, memory=[]): pass You don't even need documentation for the first example.
- paulmcpazzi 14y agoStarting from Python 3.2 there is a builtin decorator: http://docs.python.org/dev/library/functools.html#functools.lru_cache http://docs.python.org/dev/library/functools.html#functools....