
Today I was reviewing changes in Python 2.5 and I noticed the generator throw() method for the first time. While thinking about what it does and why, a question arose in my mind: Why is it called "throw"? (Yes, I know that Java and possibly other languages use this keyword!) In Python, we have long had a "raise" statement to raise exceptions. I would have thought that the generator method would have been called "raise" as well. But then I saw that it would have been impossible to implement since "raise" is a Python keyword. *Then* I wondered why "raise" is a keyword and not a function. If it were a function you could use it easily in places where today you cannot: if 'foo' == 'bar' or raise(FooBar): # only proceed if 'foo' equals 'bar' otherwise raise FooBar exception is invalid syntax because raise is not a function. Now, I can get around it: def raise_(exception): raise exception ... if 'foo' == 'bar' or raise_(FooBar): ... I have a similar question about the "assert" statement. It could possibly benefit from being a function instead. Of course, changing this would break lots of code, but maybe not any more than making print a function as in 3.0. Thoughts? -- Gerald Britton

Gerald Britton <gerald.britton@...> writes:
I find this horrible, awfully Perlish. Non-local control transfers should stick out clearly when reading source code, not be hidden at the end of a conditional. As for why raise is a keyword, I think there are several explanations: - raise is a control flow operation, as are "return", "continue", "break" and others. - raise has to create a traceback capturing the current frame stack, which is easier with a dedicated bytecode. - raise should be decently fast, which is easier with a dedicated bytecode.
I have a similar question about the "assert" statement. It could possibly benefit from being a function instead.
I think the point is that assert is entirely a no-op when the interpreter is run with "-O", while there would be a significant overhead if it was a regular function call. But I agree that the situation is less clear-cut than with the raise statement. Regards Antoine.

On Wed, Mar 18, 2009 at 1:20 PM, Antoine Pitrou <solipsis@pitrou.net> wrote:
- raise should be decently fast, which is easier with a dedicated bytecode.
Why does raise have to be decently fast? In my average case, at least, it's encountered at most once per program execution. Even if I was good about catching exceptions, the point is that they're _exceptional_ cases, so they shouldn't be happening very often. I'm not about to say raise should be a function, but I don't think it's got a huge speed requirement. -- Cheers, Leif

On Wed, Mar 18, 2009, Arnaud Delobelle wrote:
This is a standard Python idiom: try: for field in curr_fields: for item in record[field]: item = item.lower() for filter in excludes: if match(item, filter): raise Excluded except Excluded: continue -- Aahz (aahz@pythoncraft.com) <*> http://www.pythoncraft.com/ "Programming language design is not a rational science. Most reasoning about it is at best rationalization of gut feelings, and at worst plain wrong." --GvR, python-ideas, 2009-3-1

Le Wed, 18 Mar 2009 13:10:52 -0400, Gerald Britton <gerald.britton@gmail.com> s'exprima ainsi:
I'm very happy this is invalid syntax :-) I consider this kind of practice conceptual distortion. More precisely: an abuse of both (!) flow control and logical operator semantics. It reminds me of joyful (hum!) times with C routines written by "clever" people. Denis PS I would go much farther that python about logical types and operators. Lazy evaluation is ok, because the alternative is not simpler: if n != 0 and 1/n > threshold: But I'm not happy at all with the following:
I think logical operators (and or not) should accept only logical value. And logical values should not operate with numbers. ------ la vita e estrany

spir <denis.spir@free.fr> wrote:
I might be argued into agreeing with you about the first case, but it might be a logical consequence of the implementation of the second case. Or it might be an historical accident, since True used to be 1. (But the statement still gives that result in Python3, so unless it was just overlooked in the cleanup, someone must think it is a good idea.) But I would very definitely not want to give up the second example. Having the shortcut logical operators return the actual value that was last evaluated is just too darn useful :) -- R. David Murray http://www.bitdance.com

Gerald Britton wrote:
Actually, it was also called throw because it says "raise this exception over *there* (i.e inside the generator)". We're throwing the exception "over the fence" as it were. That was a rationalisation of a necessity (see the description in PEP 342), but still a good idea.
*Then* I wondered why "raise" is a keyword and not a function.
Because the compiler needs to see it and insert the appropriate commands into the bytecode to tell the interpreter to find the nearest exception handler or finally block and resume execution there. While you could probably figure out a way to do that without dedicated bytecode, I doubt it would do good things to the structure of the eval loop.
As others have said, so the compiler can drop it when optimisation is switched on. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia ---------------------------------------------------------------

Gerald Britton <gerald.britton@...> writes:
I find this horrible, awfully Perlish. Non-local control transfers should stick out clearly when reading source code, not be hidden at the end of a conditional. As for why raise is a keyword, I think there are several explanations: - raise is a control flow operation, as are "return", "continue", "break" and others. - raise has to create a traceback capturing the current frame stack, which is easier with a dedicated bytecode. - raise should be decently fast, which is easier with a dedicated bytecode.
I have a similar question about the "assert" statement. It could possibly benefit from being a function instead.
I think the point is that assert is entirely a no-op when the interpreter is run with "-O", while there would be a significant overhead if it was a regular function call. But I agree that the situation is less clear-cut than with the raise statement. Regards Antoine.

On Wed, Mar 18, 2009 at 1:20 PM, Antoine Pitrou <solipsis@pitrou.net> wrote:
- raise should be decently fast, which is easier with a dedicated bytecode.
Why does raise have to be decently fast? In my average case, at least, it's encountered at most once per program execution. Even if I was good about catching exceptions, the point is that they're _exceptional_ cases, so they shouldn't be happening very often. I'm not about to say raise should be a function, but I don't think it's got a huge speed requirement. -- Cheers, Leif

On Wed, Mar 18, 2009, Arnaud Delobelle wrote:
This is a standard Python idiom: try: for field in curr_fields: for item in record[field]: item = item.lower() for filter in excludes: if match(item, filter): raise Excluded except Excluded: continue -- Aahz (aahz@pythoncraft.com) <*> http://www.pythoncraft.com/ "Programming language design is not a rational science. Most reasoning about it is at best rationalization of gut feelings, and at worst plain wrong." --GvR, python-ideas, 2009-3-1

Le Wed, 18 Mar 2009 13:10:52 -0400, Gerald Britton <gerald.britton@gmail.com> s'exprima ainsi:
I'm very happy this is invalid syntax :-) I consider this kind of practice conceptual distortion. More precisely: an abuse of both (!) flow control and logical operator semantics. It reminds me of joyful (hum!) times with C routines written by "clever" people. Denis PS I would go much farther that python about logical types and operators. Lazy evaluation is ok, because the alternative is not simpler: if n != 0 and 1/n > threshold: But I'm not happy at all with the following:
I think logical operators (and or not) should accept only logical value. And logical values should not operate with numbers. ------ la vita e estrany

spir <denis.spir@free.fr> wrote:
I might be argued into agreeing with you about the first case, but it might be a logical consequence of the implementation of the second case. Or it might be an historical accident, since True used to be 1. (But the statement still gives that result in Python3, so unless it was just overlooked in the cleanup, someone must think it is a good idea.) But I would very definitely not want to give up the second example. Having the shortcut logical operators return the actual value that was last evaluated is just too darn useful :) -- R. David Murray http://www.bitdance.com

Gerald Britton wrote:
Actually, it was also called throw because it says "raise this exception over *there* (i.e inside the generator)". We're throwing the exception "over the fence" as it were. That was a rationalisation of a necessity (see the description in PEP 342), but still a good idea.
*Then* I wondered why "raise" is a keyword and not a function.
Because the compiler needs to see it and insert the appropriate commands into the bytecode to tell the interpreter to find the nearest exception handler or finally block and resume execution there. While you could probably figure out a way to do that without dedicated bytecode, I doubt it would do good things to the structure of the eval loop.
As others have said, so the compiler can drop it when optimisation is switched on. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia ---------------------------------------------------------------
participants (11)
-
Aahz
-
Antoine Pitrou
-
Arnaud Delobelle
-
George Sakkis
-
Gerald Britton
-
Leif Walsh
-
Nick Coghlan
-
R. David Murray
-
Raymond Hettinger
-
spir
-
Steven D'Aprano