[Python-ideas] Sublocal scoping at its simplest
Steven D'Aprano
steve at pearwood.info
Sun Apr 29 01:53:17 EDT 2018
On Sun, Apr 29, 2018 at 01:14:54PM +1000, Chris Angelico wrote:
[...]
> def f():
> e = 2.71828
> try:
> 1/0
> except Exception as e:
> print(e)
> print(e)
> f()
> I propose to change the semantics as follows:
>
> 1) Bind the caught exception to a sublocal 'e'
> 2) Execute the suite, with the reference to 'e' seeing the sublocal
> 3) Set the sublocal e to None
> 4) Unbind the sublocal e
>
> At the unindent, the sublocal name will vanish, and the original 'e'
> will reappear. Thus the final print will display 2.71828, just as it
> would if no exception had been raised.
What problem does this solve?
The current behaviour where 'e' is unbound when the except clause
finishes is a neccessary but ugly hack that forces you to do bind 'e' to
another variable if you want to inspect it after the exception:
try:
something()
except Exception as e:
err = e # defeat the automatic deletion of e
print(e)
For example, in the interactive interpreter, where I do this very
frequently.
I understand and accept the reasons for deleting e in Python 3, and
don't wish to re-debate those. But regardless of whether we have
Python 3 behaviour or Python 2 behaviour, binding to e has always
replaced the value of e. Just as if I had written:
except Exception as some_other_name:
e = some_other_name
It has never been the case that binding to e in the except clause won't
replace any existing binding to e, and I see no reason why anyone would
desire that. If you don't want to replace e, then don't use e as the
name for the exception.
Your proposal doesn't solve any known problem that I can see. For people
like me who want to inspect the error object outside the except clause,
we still have to defeat the compiler, so you're not solving anything for
me. You're not solving any problems for those people who desire (for
some reason) that except clauses are their own scope. It is only the
exception variable itself which is treated as a special case.
The one use-case you give is awfully dubious: if I wanted 'e' to keep
its value from before the exception, why on earth would I rebind 'e'
when there are approximately a zillion alternative names I could use?
Opportunities for confusion should be obvious:
e = 2.71
x = 1
try:
...
except Exception as e:
assert isinstance(e, Exception)
x = 2
assert isinstance(e, Exception) # why does this fail?
assert x != 2 # why does this fail?
Conceptually, this is even more more complex than the idea of giving the
except block its own scope. The block shares the local scope, it's just
the block header (the except ... as ... line itself) which introduces a
new scope.
--
Steve
More information about the Python-ideas
mailing list