On Tue, 24 Nov 2015 11:38 pm, Antoon Pardon wrote: > Op 19-11-15 om 13:45 schreef Steven D'Aprano: [...] >> I don't mean that it isn't sometimes useful. Of course it is sometimes >> useful, there's no doubt about that. But why would you expect the >> language to default to the *slow*, *expensive*, *complicated* behaviour >> instead of the *fast*, *cheap*, *simple* behaviour? > > I would say because it is better understandable.
I disagree that it (late-binding) is more understandable. Late-binding (or, "late evaluation" for those who prefer that term) is no easier to understand than early binding. Earlier, I publicly screwed up reasoning about late binding. And so did BartC, whose personal language has precisely the late binding semantics he wants, exactly as he designed it, and yet he was surprised by its behaviour leading him to (wrongly) conclude that his language was more dynamic than Python: Quote: "(Python returns 42; so that means my languages are more dynamic than Python? That's hard to believe!)" See this thread, post from BartC dated Fri, 20 Nov 2015 08:21:24 am. > The way default value > behave now, is just a little less surprising as when the following segment > of code would print: [1] > > a = [] > a.append(1) > b = [] > print b No, it really isn't anything like that at all. If you (generic you, not specifically you personally) think that it is similar, you've misunderstood what is going on. If we want an analogy, this is a good one: alist = [] for i in range(20): alist.append(i) print alist Are you surprised that alist keeps growing? Contrast this to: for i in range(20): alist = [] alist.append(i) print alist Are you surprised that alist keeps getting re-set to the empty list? Change the for-loop to a function declaration. If the binding of the list [] happens inside the indented block, it will happen every time the function is called (just as it happens each time through the loop in the second example above). If the binding happens outside of the body, it happens once, not every time. Folk like BartC want the function default to be re-evaluated every time the function is called. I'm not blind to the usefulness of it: I write lots of functions with late-binding semantics too. I just write them like this: def func(a=None): if a is None: a = something() Perhaps as more people start using function annotations, it will become more obvious that function declarations aren't re-evaluated every single time you call the function: def func(a:some_expression=another_expression): ... *Both* expressions, the annotation and the default, are evaluated once and once only. > Let us not forget that the tutorial talks about Default Argument *Values*. > And also the language reference talks about precomputed *values*. What > we really get is a precomputed object. There is a distinction between value and object, but without seeing the exact wording and context of the tutorial and language reference, I cannot tell whether they are misleading or not. There's nothing wrong with talking about default values. You just have to remember that some values can change. >> You already have one way to set the argument to a fresh list every time >> you call the function: put the code you want executed inside the body of >> the function. There's no need for a *second* way to get the same result. > > Which is beside the point. The point is understandability. Okay. Suppose for the sake of the argument I agree with you that "understandability" is the most important factor here. Then early binding is still the clear winner, because it has simple, easy to understand semantics: - the "def" line is executed once, and once only, including all parameter defaults and annotations; - the *body* of the function is executed when the function is called, not the "def" function declaration. Since the function defaults are part of the declaration, not the body, it is far more understandable that it is executed once only. >> But if you want the default value to be evaluated exactly once, and once >> only, there is no real alternative to early binding. You could use a >> global variable, of course, but that is no solution -- that's a problem >> waiting to happen. > > No more than the situation we have now. The situation we have now is a > problem waiting to happen too. Which is confirmed again and again. You are > stacking the deck by calling a possible alternative "a problem waiting to > happen" while ignoring the problems that happen with the current > situation. Pardon me, I certainly am not ignoring anything of the sort. I do acknowledge that the behaviour of mutable defaults can be surprising. I even admitted that it surprised me. It's often not the behaviour that you want, so you have to do something slightly different. The bottom line is that there are good reasons for the way Python works with function defaults, but they aren't good for everything. Neither is the alternative. Whichever design Python used, it would inconvenience people at some time or another. -- Steven -- https://mail.python.org/mailman/listinfo/python-list