I know that in general python only makes new scopes for classes, functions etc., but I'm confused by the as
statement in a try/except block or context manager. Variables assigned inside the block are accessible outside it, which makes sense, but the variable bound with as
itself is not.
So this fails:
try:
raise RuntimeError()
except RuntimeError as error:
pass
print(repr(error))
but this succeeds:
try:
raise RuntimeError()
except RuntimeError as e:
error = e
print(repr(error))
What's going on with the variable bound with as
, and why don't normal python scoping rules apply? The PEP indicates that it's just a normally bound python variable, but that doesn't seem to be the case.
As explained in PEP 3110, as well as current documentation, variables bound with as
in an except
block are explicitly and specially cleared at the end of the block, even though they share the same local scope. This improves the immediacy of garbage collection. The as
syntax was originally not available for exceptions in 2.x; it was backported for 2.6, but the old semantics were preserved.
The same does not apply to with
blocks:
>>> from contextlib import contextmanager
>>> @contextmanager
... def test():
... yield
...
>>> with test() as a:
... pass
...
>>> a # contains None; does not raise NameError
>>>
>>> def func(): # similarly within a function
... with test() as a:
... pass
... return a
...
>>> func()
>>>
The behaviour is specific to the except
block, not to the as
keyword.