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

jfong at ms4.hinet.net jfong at ms4.hinet.net
Thu Dec 27 04:23:06 EST 2018


eryk sun於 2018年12月27日星期四 UTC+8下午2時31分58秒寫道:
> On 12/26/18, jfong at ms4.hinet.net <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
> 
> Maybe rewriting it with approximately equivalent inline code and
> generator functions will clarify the difference:
> 
>     class Foo:
>         def genexpr1(iterable):
>             for val in iterable:
>                 yield val
> 
>         def genexpr2(iterable):
>             for i in iterable:
>                 yield XS[i]
> 
>         XS = [15, 15, 15, 15]
>         Z4 = sum(genexpr1(XS))
>         try:
>             Z5 = sum(genexpr2(range(len(XS))))
>         except NameError:
>             Z5 = None
> 
>         del genexpr1, genexpr2
> 
>     >>> print(Foo.Z4, Foo.Z5)
>     60 None
> 
> In both cases, an iterable is passed to the generator function. This
> argument is evaluated in the calling scope (e.g. range(len(XS))). A
> generator expression has a similar implementation, except it also
> evaluates the iterator for the iterable to ensure an exception is
> raised immediately in the defining scope if it's not iterable. For
> example:
> 
>     >>> (x for x in 1)
>     Traceback (most recent call last):
>       File "<stdin>", line 1, in <module>
>     TypeError: 'int' object is not iterable
> 
> genexpr1 is working with local variables only, but genexpr2 has a
> non-local reference to variable XS, which we call late binding. In
> this case, when the generator code executes the first pass of the loop
> (whenever that is), it looks for XS in the global (module) scope and
> builtins scope. It's not there, so a NameError is raised.
> 
> With late-binding, the variable can get deleted or modified in the
> source scope while the generator gets evaluated. For example:
> 
>     >>> x = 'spam'
>     >>> g = (x[i] for i in range(len(x)))
>     >>> next(g)
>     's'
>     >>> del x
>     >>> next(g)
>     Traceback (most recent call last):
>       File "<stdin>", line 1, in <module>
>       File "<stdin>", line 1, in <genexpr>
>     NameError: name 'x' is not defined
> 
>     >>> x = 'spam'
>     >>> g = (x[i] for i in range(len(x)))
>     >>> next(g)
>     's'
>     >>> x = 'eggs'
>     >>> list(g)
>     ['g', 'g', 's']

I still don't get it. When I change it to using list comprehension, the problem is still there. (it now has no late-binding variable, right? :-)

>>> class Too:
...     XS = [15, 15, 15, 15]
...     Z4 = [val for val in XS]
...     Z5 = [XS[0] for val in XS]
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in Too
  File "<stdin>", line 4, in <listcomp>
NameError: name 'XS' is not defined
>>>

The problem confuse me is that is XS a local variable of the list comprehension?
If it's then Z5 should work, if it's not then Z4 shouldn't work when it's written in generator expression.

Or, things is much more complex than I thought:-(





More information about the Python-list mailing list