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 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? 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/