
On Tue, Nov 17, 2020 at 09:55:46AM +0000, Mark Shannon wrote:
Hi,
I'm wondering why ``` x = "value" try: 1/0 except Exception as x: pass ```
does not restore "value" to x after the `except` block.
Because try, if, for, while, with etc. don't create a new scope. Only two statements create a new scope: `def` and `class`. I presume you don't also expect these to restore x after the blocks are ended: x = "value" for x in range(100): pass with open('filename') as x: text = x.read() `try...except` is no different. If you search the archives on Python-List, going back two or four years, you will find extensive discussion between those who think that every statement with an indent should create a new scope, and those who don't. Apologies in advance to supporters of the "new scope" philosophy who might feel I am giving the idea short shrift, but my recollection is that they were unable to give any better justification than "but that's what C does" and "well, it might be useful someday to use a name inside a block without clobbering its existing value". (My response to that is, just use a new variable name, it's not like there is a shortage.) 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. This is documented here: https://docs.python.org/3/reference/compound_stmts.html#the-try-statement although only briefly and without much detail.
Nor does there seem to be any good technical reason for doing it this way.
I expect the reason is the usual cost-benefit of "good enough". The effort involved in storing the old name binding and then restoring it afterwards is more than the benefit gained (almost zero). This has been part of Python since Python 3.0, so if you are only just noticing it, that gives an idea of how rarely it comes up.
Here's an example of restoring the value of the variable after the `except` block:
def f(x): ... try: ... 1/0 ... except Exception as x: ... pass ... return x ... f("hi") 'hi'
What Python interpreter are you using for that? This is not what happens in Python 3: # raises an exception UnboundLocalError: local variable 'x' referenced before assignment or Python 2.7: # returns the exception object ZeroDivisionError('integer division or modulo by zero',) So unless you are using some modified or alternative interpreter, I don't know how you got that output. -- Steve