[Python-Dev] Tangent on class level scoping rules (was Re: PEP 463: Exception-catching expressions)

Nick Coghlan ncoghlan at gmail.com
Fri Feb 21 15:42:15 CET 2014


On 22 February 2014 00:22, Rob Cliffe <rob.cliffe at btinternet.com> wrote:
> Thanks for looking into this Nick.
> I confess I don't entirely understand the technical argument (my
> understanding breaks down at "the subexpressions can't see the class level
> variables", but I don't want to waste anybody's time expecting an
> explanation, I can always look into it myself)

It's a relatively arcane scoping rule that only matters if you have
non-trivial logic at class scope. The vast majority of Python
programmers will never have to care, because they do the typical thing
and their class bodies consist almost entirely of function definitions
and relatively simple assignment statements. However, it's something
that expression level name binding proposals need to take into account
if they want to avoid side effects on the containing scope.

Python's scoping rules are deliberately designed so that methods
*can't* see class attributes directly, they have to access them via
their first argument or the magic __class__ variable (Py3 only for the
latter). For example:

>>> class C:
...     attr = "Hello"
...     def m(self):
...         print(self.attr)
...         print(attr)
...
>>> C().m()
Hello
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in m
NameError: global name 'attr' is not defined

The trick that makes it relevant here is that the "hidden" iteration
variable in comprehensions is implemented by creating an implicit
closure and then calling it. From the point of view of the scoping
rules, that implicit closure behaves the same way as any other method
definition, so most of the subexpressions can't see the class
variables. The sole exception is the outermost iterator expression,
because that is evaluated in the containing scope and then passed to
the closure as an argument (this is all based on the way generator
expressions work, and those *have* to be a closure, because they need
to run in their own completely independent frame).

As Chris later noted, you likely *could* still implement expression
local name binding for an except expression without a full closure, it
would just be rather difficult.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia


More information about the Python-Dev mailing list