Hi Chris,

On 17/11/2020 11:03 am, Chris Angelico wrote:
On Tue, Nov 17, 2020 at 9:44 PM Steven D'Aprano <st...@pearwood.info> wrote:
`try...except` is no different.
...
The only wrinkle in the case of `try...except` is that the error
variable is deleted, even if it wasn't actually used. If you look at the
byte-code generated, each compound try...except with an exception
variable is followed by the equivalent of:

     err = None
     del err


There really ought to be a FAQ about this, but it has something to do
with the exception object forming a long-lasting reference cycle. To
avoid that, the error variable is nuked on leaving the compound block.

That's a much bigger wrinkle than it might seem at first, though, and
I agree, this is a quite literal frequently-asked-question and should
be made clear somewhere. The except clause is special in that, if you
want the exception afterwards, you have to reassign it to another
variable; but it doesn't ACTUALLY introduce a subscope, despite kinda
looking like it does.

Interestingly, Python 3.10 has a very odd disassembly:

def f():
...     try: g()
...     except Exception as e:
...             print(e)
...
import dis
dis.dis(f)
   2           0 SETUP_FINALLY           10 (to 12)
               2 LOAD_GLOBAL              0 (g)
               4 CALL_FUNCTION            0
               6 POP_TOP
               8 POP_BLOCK
              10 JUMP_FORWARD            44 (to 56)

   3     >>   12 DUP_TOP
              14 LOAD_GLOBAL              1 (Exception)
              16 JUMP_IF_NOT_EXC_MATCH    54
              18 POP_TOP
              20 STORE_FAST               0 (e)
              22 POP_TOP
              24 SETUP_FINALLY           20 (to 46)

   4          26 LOAD_GLOBAL              2 (print)
              28 LOAD_FAST                0 (e)
              30 CALL_FUNCTION            1
              32 POP_TOP
              34 POP_BLOCK
              36 POP_EXCEPT
              38 LOAD_CONST               0 (None)
              40 STORE_FAST               0 (e)
              42 DELETE_FAST              0 (e)
              44 JUMP_FORWARD            10 (to 56)
         >>   46 LOAD_CONST               0 (None)
              48 STORE_FAST               0 (e)
              50 DELETE_FAST              0 (e)
              52 RERAISE
         >>   54 RERAISE
         >>   56 LOAD_CONST               0 (None)
              58 RETURN_VALUE


Reconstructing approximately equivalent Python code, this would mean
it looks something like this:

def f():
     try: g()
     except Exception as e:
         try:
             print(e)
             e = None
             del e
             raise
         finally:
             e = None
             del e
     except:
         raise
     return None


The equivalent Python is closer to this:

def f():
    try:
        g()
    except Exception as e:
        try:
            print(e)
        finally:
            e = None
            del e




I don't understand why (a) the "e = None; del e" part is duplicated,
nor (b) why the RERAISE opcodes are there in two branches, but I guess
it works out best to be explicit in there?


The reason for the seeming verbosity of the bytecode is that

try:
    body
finally:
    final

compiles to roughly:

try:
    body:
except:
    final
    raise
else:
    final

Which is why you see the duplicated sequences.

Cheers,
Mark.


Anyhow. You say that this can't come up very often because people can
go a long time without asking, but the trouble is that there are two
false interpretations that are both extremely close - either that
"except E as e:" is similar to "with E as e:", or that the except
clause creates its own scope. It's entirely possible to see supporting
evidence for your own wrong assumption and never actually know the
truth. Maybe this is going to be the next "Python has call-by-value"
vs "Python has call-by-reference" debate?

ChrisA
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/6NKGXWLRX3SD4JQDFCOR43TAXREC33GD/
Code of Conduct: http://python.org/psf/codeofconduct/

_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/FNRZWHGCXOYL7QL5QUDR5NJS76RRTY3H/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to