Re: Early and late binding [was Re: what does 'a=b=c=[]' do]
On Sat, 24 Dec 2011 09:50:04 +1100, Chris Angelico wrote: On Sat, Dec 24, 2011 at 9:32 AM, Steven D'Aprano steve+comp.lang.pyt...@pearwood.info wrote: Yes. But having to manage it *by hand* is still unclean: Well, my point was that Python's current behaviour _is_ that. Minus the managing it by hand part. * you still have to assign the default value to the function assignment outside the function, which is inelegant; C's static variables are initialized inside the function. But since Python doesn't have that, it doesn't really work that way. (You might be able to use a decorator to do some cool tricks though.) If Python were C, then static variables would be the right solution, but since it isn't, they aren't. -- Steven -- http://mail.python.org/mailman/listinfo/python-list
Re: Early and late binding [was Re: what does 'a=b=c=[]' do]
On Fri, 23 Dec 2011 19:24:44 -0500, Devin Jeanpierre wrote: To fake early binding when the language provides late binding, you still use a sentinel value, but the initialization code creating the default value is outside the body of the function, usually in a global variable: _DEFAULT_Y = [] # Private constant, don't touch. def func(x, y=None): if y is None: y = _DEFAULT_Y ... This separates parts of the code that should be together, and relies on a global, with all the disadvantages that implies. No, you can just do def func(x, y=_DEFAULT_Y): ... Point taken. Nevertheless, the semantics are still not the same as actual early binding: if the global name is deleted or changed, the function stops working. -- Steven -- http://mail.python.org/mailman/listinfo/python-list
Re: Early and late binding [was Re: what does 'a=b=c=[]' do]
On Fri, 23 Dec 2011 17:03:11 +, Neil Cerutti wrote: The disadvantage of late binding is that since the expression is live, it needs to be calculated each time, even if it turns out to be the same result. But there's no guarantee that it will return the same result each time: That's its main *advantage*. Ah yes, sorry, poor wording on my part. Whether calculating the default value *once* or *each time* is an advantage or disadvantage depends on what you're trying to do. Either way, it could be just what you want, or an annoying source of bugs. consider a default value like x=time.time(), which will return a different value each time it is called; or one like x=a+b, which will vary if either a or b are changed. Or will fail altogether if either a or b are deleted. This will surprise some people some of the time and lead to demands that Python fix the obviously buggy default argument gotcha. It's hard to see anyone being confused by the resultant exception. That's because you're coming at it from the perspective of somebody who knows what to expect, in the middle of a discussion about the semantics of late binding. Now imagine you're a newbie who has never thought about the details of when the default value is created, but has a function like def foo(x, y=a+b). He calls foo(x) seven times and it works, and on the eighth time it blows up, perhaps with a NameError. It's surprising behaviour, and newbies aren't good at diagnosing surprising bugs. Or worse, it doesn't blow up at all, but gives some invalid value that causes your calculations to be completely wrong. Exceptions are not the worst bug to have -- they are the best. It's much harder to figure out what's going wrong with an early-bound mutable. Only for those who don't understand, or aren't thinking about, Python's object model. The behaviour of early-bound mutables is obvious and clear once you think about it, but it does require you to think about what's going on under the hood, so to speak. [...] To fake early binding when the language provides late binding, you still use a sentinel value, but the initialization code creating the default value is outside the body of the function, usually in a global variable: [...] I'd use a function attribute. def func(x, y=None): if y is None: y = func.default_y ... func.default_y = [] That's awkward only if you believe function attributes are awkward. I do. All you've done is move the default from *before* the function is defined to *after* the function is defined, instead of keeping it in the function definition. It's still separate, and if the function is renamed your code stops working. In other words, it violates encapsulation of the function. That's not to say that you shouldn't do this. It's a perfectly reasonable technique, and I've used it myself, but it's not as elegant as the current Python default argument behaviour. [...] The greater efficiency was probably what decided this question for Python, right? Since late-binding is so easy to fake, is hardly ever what you want, and would make all code slower, why do it? Excellent point. -- Steven -- http://mail.python.org/mailman/listinfo/python-list
Re: Early and late binding [was Re: what does 'a=b=c=[]' do]
On Dec 24, 2:27 am, Mel Wilson mwil...@the-wire.com wrote: In a tool that's meant for other people to use to accomplish work of their own, breaking workflow is a cardinal sin. In a research language that's meant always to be up-to-date with the concept of the week, not so much. What on earth gave you the impression Python was bleeding edge? As there's more to the language than its syntax, breaking workflow disrupts the core library as much as it does the code of everyone else. More importantly, you're talking pap. Research is as much about communication as programming; if you expect every single researcher in a discipline (or even in the same _building_) to remain in perfect lockstep with the version releases of a domain-relevant language, you're either not a researcher or not a very good one. You should get out to a conference occasionally and see what people think about your concept of the week idea. -- http://mail.python.org/mailman/listinfo/python-list
Re: Early and late binding [was Re: what does 'a=b=c=[]' do]
On Dec 24, 2:15 am, Roy Smith r...@panix.com wrote: I know this is not quite the same thing, but it's interesting to look at what django (and mongoengine) do in their model definitions, prompted by your time.time() example. You can do declare a model field something like: class Foo(models.Model): timestamp = DateTimeField(default=datetime.utcnow) Now, when you create a Foo, if you don't supply a timestamp, it generates one by calling utcnow() for you. Very handy. I'm not arguing that Python should do that, just pointing out an example of where late binding is nice. There is absolutely nothing stopping you from writing functions now with that behaviour. All Python functions are early binding, late calling with their arguments, if you treat the arguments as callables within the function body. -- http://mail.python.org/mailman/listinfo/python-list
Re: Early and late binding [was Re: what does 'a=b=c=[]' do]
On Dec 24, 6:25 pm, Steven D'Aprano steve +comp.lang.pyt...@pearwood.info wrote: It's much harder to figure out what's going wrong with an early-bound mutable. Only for those who don't understand, or aren't thinking about, Python's object model. The behaviour of early-bound mutables is obvious and clear once you think about it, but it does require you to think about what's going on under the hood, so to speak. And here we've come full circle to the point of this thread. If Python was ever 'fixed' to prevent this issue, I'm pretty sure we'd see an increase in the number of questions like the OP's. -- http://mail.python.org/mailman/listinfo/python-list
Re: Early and late binding [was Re: what does 'a=b=c=[]' do]
If Python was ever 'fixed' to prevent this issue, I'm pretty sure we'd see an increase in the number of questions like the OP's. What makes you so sure? Both models do make sense and are equally valid, it's just that only one of them is true. Is it just because people already used to Python would get confused? -- Devin On Sat, Dec 24, 2011 at 9:08 AM, alex23 wuwe...@gmail.com wrote: On Dec 24, 6:25 pm, Steven D'Aprano steve +comp.lang.pyt...@pearwood.info wrote: It's much harder to figure out what's going wrong with an early-bound mutable. Only for those who don't understand, or aren't thinking about, Python's object model. The behaviour of early-bound mutables is obvious and clear once you think about it, but it does require you to think about what's going on under the hood, so to speak. And here we've come full circle to the point of this thread. If Python was ever 'fixed' to prevent this issue, I'm pretty sure we'd see an increase in the number of questions like the OP's. -- http://mail.python.org/mailman/listinfo/python-list -- http://mail.python.org/mailman/listinfo/python-list
Re: Early and late binding [was Re: what does 'a=b=c=[]' do]
On Dec 25, 9:25 am, Devin Jeanpierre jeanpierr...@gmail.com wrote: If Python was ever 'fixed' to prevent this issue, I'm pretty sure we'd see an increase in the number of questions like the OP's. What makes you so sure? Both models do make sense and are equally valid, it's just that only one of them is true. Is it just because people already used to Python would get confused? Because I believe that the source of confusion has far more to do with mutable/immutable objects than with early/late binding. Masking or 'correcting' an aspect of Python's behaviour because novices make the wrong assumption about it just pushes the problem elsewhere and potentially makes the language inconsistent at the same time. -- http://mail.python.org/mailman/listinfo/python-list
Re: Early and late binding [was Re: what does 'a=b=c=[]' do]
Because I believe that the source of confusion has far more to do with mutable/immutable objects than with early/late binding. Masking or 'correcting' an aspect of Python's behaviour because novices make the wrong assumption about it just pushes the problem elsewhere and potentially makes the language inconsistent at the same time. That seems fairly silly -- foo.append(bar) obviously mutates _something_ . Certainly it wasn't the source of my confusion when I got caught on this. What makes you believe that the fundamental confusion is about mutability? (Also, if the change is applied everywhere, the language would not be inconsistent.) -- Devin On Sat, Dec 24, 2011 at 7:10 PM, alex23 wuwe...@gmail.com wrote: On Dec 25, 9:25 am, Devin Jeanpierre jeanpierr...@gmail.com wrote: If Python was ever 'fixed' to prevent this issue, I'm pretty sure we'd see an increase in the number of questions like the OP's. What makes you so sure? Both models do make sense and are equally valid, it's just that only one of them is true. Is it just because people already used to Python would get confused? Because I believe that the source of confusion has far more to do with mutable/immutable objects than with early/late binding. Masking or 'correcting' an aspect of Python's behaviour because novices make the wrong assumption about it just pushes the problem elsewhere and potentially makes the language inconsistent at the same time. -- http://mail.python.org/mailman/listinfo/python-list -- http://mail.python.org/mailman/listinfo/python-list
Re: Early and late binding [was Re: what does 'a=b=c=[]' do]
On Dec 25, 5:32 am, Devin Jeanpierre jeanpierr...@gmail.com wrote: alex23 wrote: Because I believe that the source of confusion has far more to do with mutable/immutable objects than with early/late binding. Masking or 'correcting' an aspect of Python's behaviour because novices make the wrong assumption about it just pushes the problem elsewhere and potentially makes the language inconsistent at the same time. Thats hitting the nail on the head. That seems fairly silly -- foo.append(bar) obviously mutates _something_ . Certainly it wasn't the source of my confusion when I got caught on this. What makes you believe that the fundamental confusion is about mutability? The confusion is not about mutability. Its about mutability and parameter passing being non-orthogonal. (Also, if the change is applied everywhere, the language would not be inconsistent.) Obviously that would depend on what the change is. -- http://mail.python.org/mailman/listinfo/python-list
Re: Early and late binding [was Re: what does 'a=b=c=[]' do]
On 12/24/2011 07:25 PM, Steven D'Aprano wrote: I'd use a function attribute. def func(x, y=None): if y is None: y = func.default_y ... func.default_y = [] That's awkward only if you believe function attributes are awkward. I do. All you've done is move the default from *before* the function is defined to *after* the function is defined, instead of keeping it in the function definition. It's still separate, and if the function is renamed your code stops working. In other words, it violates encapsulation of the function. Although we can solve that (default being after the function is defined) using a simple decorator: def funcargs(**args): def __decorate_with_args(func): for k,v in args.items(): setattr(func, k, v) return func return __decorate_with_args Usage: @funcargs(foo=4) def bar(baz): return baz + bar.foo et voila, we had just reinvented early binding default argument, with a much uglier syntax. -- http://mail.python.org/mailman/listinfo/python-list
Early and late binding [was Re: what does 'a=b=c=[]' do]
On Fri, 23 Dec 2011 13:13:38 +, Neil Cerutti wrote: On 2011-12-23, Neil Cerutti ne...@norwich.edu wrote: Is the misfeature that Python doesn't evaluate the default argument expression every time you call the function? What would be the harm if it did? ...you know, assuming it wouldn't break existing code. ;) It will. Python's default argument strategy has been in use for 20 years. Some code will rely on it. I know mine does. There are two strategies for dealing with default arguments that I know of: early binding and late binding. Python has early binding: the default argument is evaluated once, when the function is created. Late binding means the default argument is always re-evaluated each time it is needed. Both strategies are reasonable choices. Both have advantages and disadvantages. Both have use-cases, and both lead to confusion when the user expects one but gets the other. If you think changing from early to late binding will completely eliminate the default argument gotcha, you haven't thought things through -- at best you might reduce the number of complaints, but only at the cost of shifting them from one set of use- cases to another. Early binding is simple to implement and simple to explain: when you define a function, the default value is evaluated once, and the result stored to be used whenever it is needed. The disadvantage is that it can lead to unexpected results for mutable arguments. Late binding is also simple to explain, but a little harder to implement. The function needs to store the default value as a piece of code (an expression) which can be re-evaluated as often as needed, not an object. The disadvantage of late binding is that since the expression is live, it needs to be calculated each time, even if it turns out to be the same result. But there's no guarantee that it will return the same result each time: consider a default value like x=time.time(), which will return a different value each time it is called; or one like x=a+b, which will vary if either a or b are changed. Or will fail altogether if either a or b are deleted. This will surprise some people some of the time and lead to demands that Python fix the obviously buggy default argument gotcha. If a language only offers one, I maintain it should offer early binding (the status quo). Why? Because it is more elegant to fake late binding in an early binding language than vice versa. To fake late binding in a language with early binding, use a sentinel value and put the default value inside the body of the function: def func(x, y=None): if y is None: y = [] ... All the important parts of the function are in one place, namely inside the function. To fake early binding when the language provides late binding, you still use a sentinel value, but the initialization code creating the default value is outside the body of the function, usually in a global variable: _DEFAULT_Y = [] # Private constant, don't touch. def func(x, y=None): if y is None: y = _DEFAULT_Y ... This separates parts of the code that should be together, and relies on a global, with all the disadvantages that implies. -- Steven -- http://mail.python.org/mailman/listinfo/python-list
Re: Early and late binding [was Re: what does 'a=b=c=[]' do]
On Sat, Dec 24, 2011 at 2:49 AM, Steven D'Aprano steve+comp.lang.pyt...@pearwood.info wrote: To fake early binding when the language provides late binding, you still use a sentinel value, but the initialization code creating the default value is outside the body of the function, usually in a global variable: _DEFAULT_Y = [] # Private constant, don't touch. def func(x, y=None): if y is None: y = _DEFAULT_Y ... This separates parts of the code that should be together, and relies on a global, with all the disadvantages that implies. A static variable (in the C sense) would make this just as clean as the alternative. In Python, that could be implemented as an attribute of the function object. Oh looky here... that's how default arguments are implemented. :) Tim Toady. ChrisA -- http://mail.python.org/mailman/listinfo/python-list
Re: Early and late binding [was Re: what does 'a=b=c=[]' do]
In article 4ef4a30d$0$29973$c3e8da3$54964...@news.astraweb.com, Steven D'Aprano steve+comp.lang.pyt...@pearwood.info wrote: The disadvantage of late binding is that since the expression is live, it needs to be calculated each time, even if it turns out to be the same result. But there's no guarantee that it will return the same result each time: consider a default value like x=time.time(), which will return a different value each time it is called I know this is not quite the same thing, but it's interesting to look at what django (and mongoengine) do in their model definitions, prompted by your time.time() example. You can do declare a model field something like: class Foo(models.Model): timestamp = DateTimeField(default=datetime.utcnow) Now, when you create a Foo, if you don't supply a timestamp, it generates one by calling utcnow() for you. Very handy. I'm not arguing that Python should do that, just pointing out an example of where late binding is nice. Technically, that's probably not really late binding. You always get the same function bound, it's just called each time it's used because django notices that you passed a callable. Maybe sort of early binding, late calling would be a more apt description, but given that python has descriptors, the line between data and function sometimes gets a little blurry anyway. -- http://mail.python.org/mailman/listinfo/python-list
Re: Early and late binding [was Re: what does 'a=b=c=[]' do]
Steven D'Aprano wrote: On Fri, 23 Dec 2011 13:13:38 +, Neil Cerutti wrote: On 2011-12-23, Neil Cerutti ne...@norwich.edu wrote: ...you know, assuming it wouldn't break existing code. ;) It will. Python's default argument strategy has been in use for 20 years. Some code will rely on it. I know mine does. In a tool that's meant for other people to use to accomplish work of their own, breaking workflow is a cardinal sin. In a research language that's meant always to be up-to-date with the concept of the week, not so much. Mel. -- http://mail.python.org/mailman/listinfo/python-list
Re: Early and late binding [was Re: what does 'a=b=c=[]' do]
On 2011-12-23, Steven D'Aprano steve+comp.lang.pyt...@pearwood.info wrote: On Fri, 23 Dec 2011 13:13:38 +, Neil Cerutti wrote: On 2011-12-23, Neil Cerutti ne...@norwich.edu wrote: Is the misfeature that Python doesn't evaluate the default argument expression every time you call the function? What would be the harm if it did? ...you know, assuming it wouldn't break existing code. ;) It will. Python's default argument strategy has been in use for 20 years. Some code will rely on it. I know mine does. I'm aware of that. I should have put the question differently, but you did guess what I meant to ask. Thanks for the dicussion. Early binding is simple to implement and simple to explain: when you define a function, the default value is evaluated once, and the result stored to be used whenever it is needed. The disadvantage is that it can lead to unexpected results for mutable arguments. Late binding is also simple to explain, but a little harder to implement. The function needs to store the default value as a piece of code (an expression) which can be re-evaluated as often as needed, not an object. The disadvantage of late binding is that since the expression is live, it needs to be calculated each time, even if it turns out to be the same result. But there's no guarantee that it will return the same result each time: That's its main *advantage*. consider a default value like x=time.time(), which will return a different value each time it is called; or one like x=a+b, which will vary if either a or b are changed. Or will fail altogether if either a or b are deleted. This will surprise some people some of the time and lead to demands that Python fix the obviously buggy default argument gotcha. It's hard to see anyone being confused by the resultant exception. It's much harder to figure out what's going wrong with an early-bound mutable. If a language only offers one, I maintain it should offer early binding (the status quo). Why? Because it is more elegant to fake late binding in an early binding language than vice versa. To fake late binding in a language with early binding, use a sentinel value and put the default value inside the body of the function: def func(x, y=None): if y is None: y = [] ... All the important parts of the function are in one place, namely inside the function. To fake early binding when the language provides late binding, you still use a sentinel value, but the initialization code creating the default value is outside the body of the function, usually in a global variable: _DEFAULT_Y = [] # Private constant, don't touch. def func(x, y=None): if y is None: y = _DEFAULT_Y ... This separates parts of the code that should be together, and relies on a global, with all the disadvantages that implies. I'd use a function attribute. def func(x, y=None): if y is None: y = func.default_y ... func.default_y = [] That's awkward only if you believe function attributes are awkward. Even if common practice were instead to use a global variable, as you did, it wouldn't be considered bad if it were a common idiom. In case, a global that's not meant to be rebound or mutated is the best possible variety. However, I also wouldn't base the decision of late versus early binding on how confusing it would be if you assume wrongly which one you are getting. Most default function arguments are literals, so in Python the question just doesn't come up until you get mutabled. The greater efficiency was probably what decided this question for Python, right? Since late-binding is so easy to fake, is hardly ever what you want, and would make all code slower, why do it? -- Neil Cerutti -- http://mail.python.org/mailman/listinfo/python-list
Re: Early and late binding [was Re: what does 'a=b=c=[]' do]
On Sat, 24 Dec 2011 02:55:41 +1100, Chris Angelico wrote: On Sat, Dec 24, 2011 at 2:49 AM, Steven D'Aprano steve+comp.lang.pyt...@pearwood.info wrote: To fake early binding when the language provides late binding, you still use a sentinel value, but the initialization code creating the default value is outside the body of the function, usually in a global variable: _DEFAULT_Y = [] # Private constant, don't touch. def func(x, y=None): if y is None: y = _DEFAULT_Y ... This separates parts of the code that should be together, and relies on a global, with all the disadvantages that implies. A static variable (in the C sense) would make this just as clean as the alternative. In Python, that could be implemented as an attribute of the function object. Oh looky here... that's how default arguments are implemented. :) Yes. But having to manage it *by hand* is still unclean: * you still have to assign the default value to the function assignment outside the function, which is inelegant; * if you rename the function, the internal lookup func.default_value will fail. I'm not saying it can't be done. I'm just saying that the status quo is cleaner and more elegant, and if you want late binding, there is a simple, obvious, elegant idiom to get it. -- Steven -- http://mail.python.org/mailman/listinfo/python-list
Re: Early and late binding [was Re: what does 'a=b=c=[]' do]
On Sat, Dec 24, 2011 at 9:32 AM, Steven D'Aprano steve+comp.lang.pyt...@pearwood.info wrote: Yes. But having to manage it *by hand* is still unclean: Well, my point was that Python's current behaviour _is_ that. * you still have to assign the default value to the function assignment outside the function, which is inelegant; C's static variables are initialized inside the function. But since Python doesn't have that, it doesn't really work that way. (You might be able to use a decorator to do some cool tricks though.) * if you rename the function, the internal lookup func.default_value will fail. Python would benefit some from a current-function reference, if tricks like this were to be encouraged. It'd help with recursion too. h... def Y(func): return lambda *args,**kwargs: func(func,*args,**kwargs) @Y def foo(me,x): if x5: return x return me(me,x+1),7,x foo(3) (((6, 7, 5), 7, 4), 7, 3) Useful? Not very. Maybe as a language feature, but not as a parameter. But of curiosity value. ChrisA -- http://mail.python.org/mailman/listinfo/python-list
Re: Early and late binding [was Re: what does 'a=b=c=[]' do]
To fake early binding when the language provides late binding, you still use a sentinel value, but the initialization code creating the default value is outside the body of the function, usually in a global variable: _DEFAULT_Y = [] # Private constant, don't touch. def func(x, y=None): if y is None: y = _DEFAULT_Y ... This separates parts of the code that should be together, and relies on a global, with all the disadvantages that implies. No, you can just do def func(x, y=_DEFAULT_Y): ... -- Devin On Fri, Dec 23, 2011 at 10:49 AM, Steven D'Aprano steve+comp.lang.pyt...@pearwood.info wrote: On Fri, 23 Dec 2011 13:13:38 +, Neil Cerutti wrote: On 2011-12-23, Neil Cerutti ne...@norwich.edu wrote: Is the misfeature that Python doesn't evaluate the default argument expression every time you call the function? What would be the harm if it did? ...you know, assuming it wouldn't break existing code. ;) It will. Python's default argument strategy has been in use for 20 years. Some code will rely on it. I know mine does. There are two strategies for dealing with default arguments that I know of: early binding and late binding. Python has early binding: the default argument is evaluated once, when the function is created. Late binding means the default argument is always re-evaluated each time it is needed. Both strategies are reasonable choices. Both have advantages and disadvantages. Both have use-cases, and both lead to confusion when the user expects one but gets the other. If you think changing from early to late binding will completely eliminate the default argument gotcha, you haven't thought things through -- at best you might reduce the number of complaints, but only at the cost of shifting them from one set of use- cases to another. Early binding is simple to implement and simple to explain: when you define a function, the default value is evaluated once, and the result stored to be used whenever it is needed. The disadvantage is that it can lead to unexpected results for mutable arguments. Late binding is also simple to explain, but a little harder to implement. The function needs to store the default value as a piece of code (an expression) which can be re-evaluated as often as needed, not an object. The disadvantage of late binding is that since the expression is live, it needs to be calculated each time, even if it turns out to be the same result. But there's no guarantee that it will return the same result each time: consider a default value like x=time.time(), which will return a different value each time it is called; or one like x=a+b, which will vary if either a or b are changed. Or will fail altogether if either a or b are deleted. This will surprise some people some of the time and lead to demands that Python fix the obviously buggy default argument gotcha. If a language only offers one, I maintain it should offer early binding (the status quo). Why? Because it is more elegant to fake late binding in an early binding language than vice versa. To fake late binding in a language with early binding, use a sentinel value and put the default value inside the body of the function: def func(x, y=None): if y is None: y = [] ... All the important parts of the function are in one place, namely inside the function. To fake early binding when the language provides late binding, you still use a sentinel value, but the initialization code creating the default value is outside the body of the function, usually in a global variable: _DEFAULT_Y = [] # Private constant, don't touch. def func(x, y=None): if y is None: y = _DEFAULT_Y ... This separates parts of the code that should be together, and relies on a global, with all the disadvantages that implies. -- Steven -- http://mail.python.org/mailman/listinfo/python-list -- http://mail.python.org/mailman/listinfo/python-list