[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