Ask for help about class variable scope (Re: Why doesn't a dictionary work in classes?)

Chris Angelico rosuav at gmail.com
Thu Dec 27 12:37:55 EST 2018


On Fri, Dec 28, 2018 at 3:31 AM Ian Kelly <ian.g.kelly at gmail.com> wrote:
>
> On Wed, Dec 26, 2018 at 11:21 PM Chris Angelico <rosuav at gmail.com> wrote:
> >
> > On Thu, Dec 27, 2018 at 1:56 PM <jfong at ms4.hinet.net> wrote:
> > >
> > > I saw the code below at stackoverflow. I have a little idea about the scope of a class, and list comprehension and generator expressions, but still can't figure out why Z4 works and Z5 not. Can someone explain it? (in a not-too-complicated way:-)
> > >
> > > class Foo():
> > >     XS = [15, 15, 15, 15]
> > >     Z4 = sum(val for val in XS)
> > >     try:
> > >         Z5 = sum(XS[i] for i in range(len(XS)))
> > >     except NameError:
> > >         Z5 = None
> > >
> > > print(Foo.Z4, Foo.Z5)
> > > >>> 60 None
> > >
> >
> > Class scope is special, and a generator expression within that class
> > scope is special too. There have been proposals to make these kinds of
> > things less special, but the most important thing to remember is that
> > when you create a generator expression, it is actually a function.
> > Remember that a function inside a class statement becomes a method,
> > and that inside the method, you have to use "self.X" rather than just
> > "X" to reference class attributes. That's what's happening here.
>
> Except you can't use "self" either because the class doesn't exist yet
> at the time the generator expression is being evaluated.

Well, yes. But that's the easiest way to highlight the difference of scope.

> Nor can you
> use "self" generally even if you wait until the class does exist
> before you iterate over the generator, because while a generator
> expression may scope like a function, and may under the hood be
> implemented as a function, a generator object is not a function object
> and will not create a method.

Hmm. It creates a function which is called at class scope. Based on
disassembly, I can't see a difference between the function created for
a genexp and the one created for a method. What IS true, though, is
that there's no way to pass arguments to the genexp, which means that
it won't be passed a 'self'. So if you wait till the class exists
before iterating over the generator, you can name the class, but you
can't use self:

>>> class Foo:
...     word = "spam"
...     genexp = (l.upper() for shim in [...] for l in Foo.word)
...
>>> list(Foo.genexp)
['S', 'P', 'A', 'M']

I would personally have preferred for the official Python language
definition to have described comprehensions and genexps as creating a
scope boundary, without baking into the language "they're hidden,
implicit functions". But that ship has sailed, and when I debated the
point, all Pandora's box got opened.

ChrisA


More information about the Python-list mailing list