Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #64967 > unrolled thread
| Started by | Jessica Ross <deathweasel@gmail.com> |
|---|---|
| First post | 2014-01-29 21:56 -0800 |
| Last post | 2014-02-01 03:58 +0200 |
| Articles | 12 — 11 participants |
Back to article view | Back to comp.lang.python
Try-except-finally paradox Jessica Ross <deathweasel@gmail.com> - 2014-01-29 21:56 -0800
Re: Try-except-finally paradox Ian Kelly <ian.g.kelly@gmail.com> - 2014-01-29 23:23 -0700
Re: Try-except-finally paradox Andrew Berg <robotsondrugs@gmail.com> - 2014-01-30 00:33 -0600
Re: Try-except-finally paradox Rotwang <sg552@hotmail.co.uk> - 2014-01-30 18:12 +0000
Re: Try-except-finally paradox Ethan Furman <ethan@stoneleaf.us> - 2014-01-30 10:30 -0800
Re: Try-except-finally paradox wxjmfauth@gmail.com - 2014-01-29 22:59 -0800
Re:Try-except-finally paradox Dave Angel <davea@davea.name> - 2014-01-30 07:05 -0500
Re: Try-except-finally paradox Chris Angelico <rosuav@gmail.com> - 2014-01-31 00:02 +1100
Re: Try-except-finally paradox MRAB <python@mrabarnett.plus.com> - 2014-01-30 13:11 +0000
Re: Try-except-finally paradox Chris Angelico <rosuav@gmail.com> - 2014-01-31 00:19 +1100
Re: Try-except-finally paradox Terry Reedy <tjreedy@udel.edu> - 2014-01-31 00:26 -0500
Re: Try-except-finally paradox Göktuğ Kayaalp <self@gkayaalp.com> - 2014-02-01 03:58 +0200
| From | Jessica Ross <deathweasel@gmail.com> |
|---|---|
| Date | 2014-01-29 21:56 -0800 |
| Subject | Try-except-finally paradox |
| Message-ID | <9314ac52-a2be-4382-94ef-2c291f32be1a@googlegroups.com> |
I found something like this in a StackOverflow discussion.
>>> def paradox():
... try:
... raise Exception("Exception raised during try")
... except:
... print "Except after try"
... return True
... finally:
... print "Finally"
... return False
... return None
...
>>> return_val = paradox()
Except after try
Finally
>>> return_val
False
I understand most of this.
What I don't understand is why this returns False rather than True. Does the finally short-circuit the return in the except block?
[toc] | [next] | [standalone]
| From | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| Date | 2014-01-29 23:23 -0700 |
| Message-ID | <mailman.6110.1391063030.18130.python-list@python.org> |
| In reply to | #64967 |
[Multipart message — attachments visible in raw view] — view raw
On Jan 29, 2014 11:01 PM, "Jessica Ross" <deathweasel@gmail.com> wrote:
>
> I found something like this in a StackOverflow discussion.
> >>> def paradox():
> ... try:
> ... raise Exception("Exception raised during try")
> ... except:
> ... print "Except after try"
> ... return True
> ... finally:
> ... print "Finally"
> ... return False
> ... return None
> ...
> >>> return_val = paradox()
> Except after try
> Finally
> >>> return_val
> False
>
> I understand most of this.
> What I don't understand is why this returns False rather than True. Does
the finally short-circuit the return in the except block?
The docs don't seem to specify what happens in this case, but this behavior
is intuitive to me. If the except suite had raised an exception instead of
returning, the return in the finally would suppress that. The generalized
rule appears to be that the control flow specification executed later
overrides the earlier.
[toc] | [prev] | [next] | [standalone]
| From | Andrew Berg <robotsondrugs@gmail.com> |
|---|---|
| Date | 2014-01-30 00:33 -0600 |
| Message-ID | <mailman.6111.1391063632.18130.python-list@python.org> |
| In reply to | #64967 |
On 2014.01.29 23:56, Jessica Ross wrote:
> I found something like this in a StackOverflow discussion.
>>>> def paradox():
> ... try:
> ... raise Exception("Exception raised during try")
> ... except:
> ... print "Except after try"
> ... return True
> ... finally:
> ... print "Finally"
> ... return False
> ... return None
> ...
>>>> return_val = paradox()
> Except after try
> Finally
>>>> return_val
> False
>
> I understand most of this.
> What I don't understand is why this returns False rather than True. Does the finally short-circuit the return in the except block?
>
My guess would be that the interpreter doesn't let the finally block get skipped under any circumstances, so the return value gets set to
True, but then it forces the finally block to be run before returning, which changes the return value to False.
--
CPython 3.3.2 | Windows NT 6.2.9200 / FreeBSD 10.0
[toc] | [prev] | [next] | [standalone]
| From | Rotwang <sg552@hotmail.co.uk> |
|---|---|
| Date | 2014-01-30 18:12 +0000 |
| Message-ID | <lce4mr$p4b$1@dont-email.me> |
| In reply to | #64969 |
On 30/01/2014 06:33, Andrew Berg wrote:
> On 2014.01.29 23:56, Jessica Ross wrote:
>> I found something like this in a StackOverflow discussion.
>>>>> def paradox():
>> ... try:
>> ... raise Exception("Exception raised during try")
>> ... except:
>> ... print "Except after try"
>> ... return True
>> ... finally:
>> ... print "Finally"
>> ... return False
>> ... return None
>> ...
>>>>> return_val = paradox()
>> Except after try
>> Finally
>>>>> return_val
>> False
>>
>> I understand most of this.
>> What I don't understand is why this returns False rather than True.
>> Does the finally short-circuit the return in the except block?
>>
> My guess would be that the interpreter doesn't let the finally block
> get skipped under any circumstances, so the return value gets set to
> True, but then it forces the finally block to be run before returning,
> which changes the return value to False.
Mine too. We can check that the interpreter gets as far as evaluating
the return value in the except block:
>>> def paradox2():
try:
raise Exception("Raise")
except:
print("Except")
return [print("Return"), True][1]
finally:
print("Finally")
return False
return None
>>> ret = paradox2()
Except
Return
Finally
>>> ret
False
[toc] | [prev] | [next] | [standalone]
| From | Ethan Furman <ethan@stoneleaf.us> |
|---|---|
| Date | 2014-01-30 10:30 -0800 |
| Message-ID | <mailman.6157.1391107943.18130.python-list@python.org> |
| In reply to | #65032 |
On 01/30/2014 10:12 AM, Rotwang wrote:
> On 30/01/2014 06:33, Andrew Berg wrote:
>> On 2014.01.29 23:56, Jessica Ross wrote:
>>>
>>> I found something like this in a StackOverflow discussion.
>>> --> def paradox():
>>> ... try:
>>> ... raise Exception("Exception raised during try")
>>> ... except:
>>> ... print "Except after try"
>>> ... return True
>>> ... finally:
>>> ... print "Finally"
>>> ... return False
>>> ... return None
>>> ...
>>> --> return_val = paradox()
>>> Except after try
>>> Finally
>>> --> return_val
>>> False
>>>
>>> I understand most of this.
>>> What I don't understand is why this returns False rather than True.
>>> Does the finally short-circuit the return in the except block?
>>>
>> My guess would be that the interpreter doesn't let the finally block
>> get skipped under any circumstances, so the return value gets set to
>> True, but then it forces the finally block to be run before returning,
>> which changes the return value to False.
>
> Mine too. We can check that the interpreter gets as far as evaluating the return value in the except block:
>
> --> def paradox2():
> try:
> raise Exception("Raise")
> except:
> print("Except")
> return [print("Return"), True][1]
> finally:
> print("Finally")
> return False
> return None
>
> --> ret = paradox2()
> Except
> Return
> Finally
> --> ret
> False
And just to be thorough, if the finally block doesn't have a return:
--> def paradox3():
try:
raise Exception("Raise")
except:
print("Except")
return [print("Return"), True][1]
finally:
print("Finally")
return None
--> print(paradox3())
Except
Return
Finally
True
--
~Ethan~
[toc] | [prev] | [next] | [standalone]
| From | wxjmfauth@gmail.com |
|---|---|
| Date | 2014-01-29 22:59 -0800 |
| Message-ID | <17d88e10-c8ae-4bec-a370-8c797073caab@googlegroups.com> |
| In reply to | #64967 |
Le jeudi 30 janvier 2014 06:56:16 UTC+1, Jessica Ross a écrit :
> I found something like this in a StackOverflow discussion.
>
> >>> def paradox():
>
> ... try:
>
> ... raise Exception("Exception raised during try")
>
> ... except:
>
> ... print "Except after try"
>
> ... return True
>
> ... finally:
>
> ... print "Finally"
>
> ... return False
>
> ... return None
>
> ...
>
> >>> return_val = paradox()
>
> Except after try
>
> Finally
>
> >>> return_val
>
> False
>
>
>
> I understand most of this.
>
> What I don't understand is why this returns False rather than True. Does the finally short-circuit the return in the except block?
========
The paradox is, in my mind, that the fct paradox() is
programmed to be paradoxal.
Compare with:
>>> def noparadox(i):
... try:
... a = 1 / i
... print('Process')
... except ZeroDivisionError:
... print("ZeroDivisionError")
... a = '?'
... except Exception:
... print("Exception")
... a = '?'
... finally:
... print("Finally")
... return a
...
>>> noparadox(2)
Process
Finally
0.5
>>> noparadox(0)
ZeroDivisionError
Finally
'?'
>>> noparadox('asdf')
Exception
Finally
'?'
>>>
jmf
[toc] | [prev] | [next] | [standalone]
| From | Dave Angel <davea@davea.name> |
|---|---|
| Date | 2014-01-30 07:05 -0500 |
| Message-ID | <mailman.6126.1391083371.18130.python-list@python.org> |
| In reply to | #64967 |
Jessica Ross <deathweasel@gmail.com> Wrote in message:
> I found something like this in a StackOverflow discussion.
>>>> def paradox():
> ... try:
> ... raise Exception("Exception raised during try")
> ... except:
> ... print "Except after try"
> ... return True
> ... finally:
> ... print "Finally"
> ... return False
> ... return None
> ...
>>>> return_val = paradox()
> Except after try
> Finally
>>>> return_val
> False
>
> I understand most of this.
> What I don't understand is why this returns False rather than True. Does the finally short-circuit the return in the except block?
>
The finally has to happen before any return inside the try or the
except. And once you're in the finally clause you'll finish it
before resuming the except clause. Since it has a return, that
will happen before the other returns. The one in the except block
will never get reached.
It's the only reasonable behavior., to my mind.
--
DaveA
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2014-01-31 00:02 +1100 |
| Message-ID | <mailman.6130.1391086942.18130.python-list@python.org> |
| In reply to | #64967 |
On Thu, Jan 30, 2014 at 11:05 PM, Dave Angel <davea@davea.name> wrote:
> The finally has to happen before any return inside the try or the
> except. And once you're in the finally clause you'll finish it
> before resuming the except clause. Since it has a return, that
> will happen before the other returns. The one in the except block
> will never get reached.
>
> It's the only reasonable behavior., to my mind.
It's arguable that putting a return inside a finally is unreasonable
behaviour, but that's up to the programmer. A finally clause can be
used to do what might be done in C++ with a destructor: "no matter how
this function/block exits, do this as you unwind the stack". In C++, I
might open a file like this:
void func()
{
ofstream output("output.txt");
// do a whole lot of stuff ...
// at the close brace, output.~output() will be called, which will
close the file
}
In Python, the equivalent would be:
def func():
try:
output = open("output.txt", "w")
# do a whole lot of stuff ...
finally:
output.close()
(Actually, the Python equivalent would be to use a 'with' clause for
brevity, but 'with' uses try/finally under the covers, so it comes to
the same thing.) The concept of the finally clause is: "Whether
execution runs off the end, hits a return statement, or throws an
exception, I need you do this before anything else happens". Having a
return statement inside 'finally' as well as in 'try' is a bit of a
corner case, since you're now saying "Before you finish this function
and return something, I need you to return something else", which
doesn't usually make sense. If you think Python's behaviour is
confusing, first figure out what you would expect to happen in this
situation :)
ChrisA
[toc] | [prev] | [next] | [standalone]
| From | MRAB <python@mrabarnett.plus.com> |
|---|---|
| Date | 2014-01-30 13:11 +0000 |
| Message-ID | <mailman.6131.1391087514.18130.python-list@python.org> |
| In reply to | #64967 |
On 2014-01-30 13:02, Chris Angelico wrote:
> On Thu, Jan 30, 2014 at 11:05 PM, Dave Angel <davea@davea.name> wrote:
>> The finally has to happen before any return inside the try or the
>> except. And once you're in the finally clause you'll finish it
>> before resuming the except clause. Since it has a return, that
>> will happen before the other returns. The one in the except block
>> will never get reached.
>>
>> It's the only reasonable behavior., to my mind.
>
> It's arguable that putting a return inside a finally is unreasonable
> behaviour, but that's up to the programmer. A finally clause can be
> used to do what might be done in C++ with a destructor: "no matter how
> this function/block exits, do this as you unwind the stack". In C++, I
> might open a file like this:
>
> void func()
> {
> ofstream output("output.txt");
> // do a whole lot of stuff ...
> // at the close brace, output.~output() will be called, which will
> close the file
> }
>
> In Python, the equivalent would be:
>
> def func():
> try:
> output = open("output.txt", "w")
> # do a whole lot of stuff ...
> finally:
> output.close()
>
> (Actually, the Python equivalent would be to use a 'with' clause for
> brevity, but 'with' uses try/finally under the covers, so it comes to
> the same thing.) The concept of the finally clause is: "Whether
> execution runs off the end, hits a return statement, or throws an
> exception, I need you do this before anything else happens". Having a
> return statement inside 'finally' as well as in 'try' is a bit of a
> corner case, since you're now saying "Before you finish this function
> and return something, I need you to return something else", which
> doesn't usually make sense. If you think Python's behaviour is
> confusing, first figure out what you would expect to happen in this
> situation :)
>
One of the reasons that the 'with' statement was added was to prevent
the mistake that you've just done. ;-)
What if the file can't be opened?
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2014-01-31 00:19 +1100 |
| Message-ID | <mailman.6132.1391087975.18130.python-list@python.org> |
| In reply to | #64967 |
On Fri, Jan 31, 2014 at 12:11 AM, MRAB <python@mrabarnett.plus.com> wrote:
> One of the reasons that the 'with' statement was added was to prevent
> the mistake that you've just done. ;-)
>
> What if the file can't be opened?
Yeah, whoops. The open shouldn't be inside try/finally.
def func():
output = open("output.txt", "w")
try:
# do a whole lot of stuff ...
finally:
output.close()
But my point still stands, I believe :)
ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Terry Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2014-01-31 00:26 -0500 |
| Message-ID | <mailman.6194.1391145984.18130.python-list@python.org> |
| In reply to | #64967 |
On 1/30/2014 7:05 AM, Dave Angel wrote:
> Jessica Ross <deathweasel@gmail.com> Wrote in message:
>> I found something like this in a StackOverflow discussion.
>>>>> def paradox():
>> ... try:
>> ... raise Exception("Exception raised during try")
>> ... except:
>> ... print "Except after try"
>> ... return True
>> ... finally:
>> ... print "Finally"
>> ... return False
>> ... return None
>> ...
>>>>> return_val = paradox()
>> Except after try
>> Finally
>>>>> return_val
>> False
>>
>> I understand most of this.
>> What I don't understand is why this returns False rather than True. Does the finally short-circuit the return in the except block?
>>
>
> The finally has to happen before any return inside the try or the
> except. And once you're in the finally clause you'll finish it
> before resuming the except clause. Since it has a return, that
> will happen before the other returns. The one in the except block
> will never get reached.
>
> It's the only reasonable behavior., to my mind.
Checking with the disassembled code, it appears that the except return
happens first and is then caught and the value over-written
2 0 SETUP_FINALLY 45 (to 48)
3 SETUP_EXCEPT 16 (to 22)
3 6 LOAD_GLOBAL 0 (Exception)
9 LOAD_CONST 1 ('Exception raised during try')
12 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
15 RAISE_VARARGS 1
18 POP_BLOCK
19 JUMP_FORWARD 22 (to 44)
4 >> 22 POP_TOP
23 POP_TOP
24 POP_TOP
5 25 LOAD_GLOBAL 1 (print)
28 LOAD_CONST 2 ('Except after try')
31 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
34 POP_TOP
6 35 LOAD_CONST 3 (True)
38 RETURN_VALUE
39 POP_EXCEPT
40 JUMP_FORWARD 1 (to 44)
43 END_FINALLY
>> 44 POP_BLOCK
45 LOAD_CONST 0 (None)
8 >> 48 LOAD_GLOBAL 1 (print)
51 LOAD_CONST 4 ('Finally')
54 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
57 POP_TOP
9 58 LOAD_CONST 5 (False)
61 RETURN_VALUE
62 END_FINALLY
10 63 LOAD_CONST 0 (None)
66 RETURN_VALUE
--
Terry Jan Reedy
[toc] | [prev] | [next] | [standalone]
| From | Göktuğ Kayaalp <self@gkayaalp.com> |
|---|---|
| Date | 2014-02-01 03:58 +0200 |
| Message-ID | <mailman.6247.1391223064.18130.python-list@python.org> |
| In reply to | #64967 |
Terry Reedy <tjreedy@udel.edu> writes:
I do not have any information on the topic, but I *imagine* that the
when RETURN_VALUE opcode is evaluated within the context of an except
block, it triggers a check for whether a corresponding finally block
exists and should it exist, it is triggered, much like a callback.
So, my *imaginary* algorithm works like this:
return:
if within an except block EB:
if EB has a correspoinding final block FB
run FB
else
do return
else
do return
In Jessica's example, as the finally block executes a RETURN_VALUE
opcode, and as this is *probably* a jump to the caller, the rest of the
code does not get a chance to be executed.
As I have said earlier, I by no means assert the truth of this idea I've
explained here, but it seems quite reasonable to me.
gk
> On 1/30/2014 7:05 AM, Dave Angel wrote:
>> Jessica Ross <deathweasel@gmail.com> Wrote in message:
>>> I found something like this in a StackOverflow discussion.
>>>>>> def paradox():
>>> ... try:
>>> ... raise Exception("Exception raised during try")
>>> ... except:
>>> ... print "Except after try"
>>> ... return True
>>> ... finally:
>>> ... print "Finally"
>>> ... return False
>>> ... return None
>>> ...
>>>>>> return_val = paradox()
>>> Except after try
>>> Finally
>>>>>> return_val
>>> False
>>>
>>> I understand most of this.
>>> What I don't understand is why this returns False rather than True. Does the finally short-circuit the return in the except block?
>>>
>>
>> The finally has to happen before any return inside the try or the
>> except. And once you're in the finally clause you'll finish it
>> before resuming the except clause. Since it has a return, that
>> will happen before the other returns. The one in the except block
>> will never get reached.
>>
>> It's the only reasonable behavior., to my mind.
>
> Checking with the disassembled code, it appears that the except return
> happens first and is then caught and the value over-written
>
> 2 0 SETUP_FINALLY 45 (to 48)
> 3 SETUP_EXCEPT 16 (to 22)
>
> 3 6 LOAD_GLOBAL 0 (Exception)
> 9 LOAD_CONST 1 ('Exception raised during try')
> 12 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
> 15 RAISE_VARARGS 1
> 18 POP_BLOCK
> 19 JUMP_FORWARD 22 (to 44)
>
> 4 >> 22 POP_TOP
> 23 POP_TOP
> 24 POP_TOP
>
> 5 25 LOAD_GLOBAL 1 (print)
> 28 LOAD_CONST 2 ('Except after try')
> 31 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
> 34 POP_TOP
>
> 6 35 LOAD_CONST 3 (True)
> 38 RETURN_VALUE
> 39 POP_EXCEPT
> 40 JUMP_FORWARD 1 (to 44)
> 43 END_FINALLY
> >> 44 POP_BLOCK
> 45 LOAD_CONST 0 (None)
>
> 8 >> 48 LOAD_GLOBAL 1 (print)
> 51 LOAD_CONST 4 ('Finally')
> 54 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
> 57 POP_TOP
>
> 9 58 LOAD_CONST 5 (False)
> 61 RETURN_VALUE
> 62 END_FINALLY
>
> 10 63 LOAD_CONST 0 (None)
> 66 RETURN_VALUE
>
>
>
>
> --
> Terry Jan Reedy
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web