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

2018-12-28 Thread jfong
jf...@ms4.hinet.net於 2018年12月28日星期五 UTC+8下午4時04分07秒寫道:
> eryk sun at 2018/12/27 UTC+8 PM 6:58:33 wrote:
> > On 12/27/18, jf...@ms4.hinet.net  wrote:
> > >
> > > 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 "", line 1, in 
> > >   File "", line 4, in Too
> > >   File "", line 4, in 
> > > NameError: name 'XS' is not defined
> > >
> > > The problem confuse me is that is XS a local variable of the list
> > > comprehension?
> > 
> > XS is not a local variable in the scope of either comprehension. XS is
> > local to the class statement's scope. For each comprehension, an
> > iterator for the current object referenced by XS gets instantiated
> > (early binding) and passed as an argument to the comprehension scope.
> > If we disassemble the comprehension code, we find that this iterator
> > argument has the creatively illegal name ".0". (The bytecode
> > references it by its fast-locals-array index, not its weird name.)
> > 
> > In the Z5 case, XS is a non-local variable (late binding) in the
> > loop-body expression (left-hand side) of the comprehension. That's
> > common for Python code. In every iteration of the loop, the
> > interpreter looks up the object referenced by the name "XS", which can
> > change at any time (e.g. by another thread).
> 
> In Python document 4.2.2. Resolution of names, the last paragraph:
> 
> "...A class definition is an executable statement that may use and define 
> names. These references follow the normal rules for name resolution with an 
> exception that unbound local variables are looked up in the global namespace. 
> ...The scope of names defined in a class block is limited to the class block; 
> it does not extend to the code blocks of methods – this includes 
> comprehensions and generator expressions since they are implemented using a 
> function scope". 
> 
> These statements reflect the following difference:
> 
> >>> xy = [1,2]
> >>> [dir() for i in xy]
> [['.0', 'i'], ['.0', 'i']]
> >>> [xy[0] for i in xy]
> [1, 1]
> 
> >>> class foo():
> ... xs = [1,2]
> ... z4 = [dir() for i in xs]
> ...
> >>> foo().z4
> [['.0', 'i'], ['.0', 'i']]
> >>> class foo():
> ... xs = [1,2]
> ... z4 = [xs[0] for i in xs]
> ...
> Traceback (most recent call last):
>   File "", line 1, in 
>   File "", line 3, in foo
>   File "", line 3, in 
> NameError: name 'xs' is not defined
> >>>
> 
> and it goes further:
> 
> >>> [dir() for i in xy for j in xy]
> [['.0', 'i', 'j'], ['.0', 'i', 'j'], ['.0', 'i', 'j'], ['.0', 'i', 'j']]
> >>> class foo():
> ... xs = [1,2]
> ... z5 = [dir() for i in xs for j in xs]
> ...
> Traceback (most recent call last):
>   File "", line 1, in 
>   File "", line 3, in foo
>   File "", line 3, in 
> NameError: name 'xs' is not defined
> >>>
> 
> That's all I had learn so far, although not understand the design decision 
> behind it yet:-(
> 
> --Jach

Anyway, it "looks" weird. Isn't it?

>>> xs = [5,6,7,8]
>>> class foo():
... xs = [1,2,3]
... z4 = [xs[i] for i in xs]
...
>>> foo.z4
[6,7,8]

xs in the "for" statement referenced to class variable xs and xs[i] in the body 
referenced to the global xs with i from locals.

PS. Don't bother to reply. This is just for my own documentary:-)
-- 
https://mail.python.org/mailman/listinfo/python-list


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

2018-12-28 Thread jfong
eryk sun at 2018/12/27 UTC+8 PM 6:58:33 wrote:
> On 12/27/18, jf...@ms4.hinet.net  wrote:
> >
> > 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 "", line 1, in 
> >   File "", line 4, in Too
> >   File "", line 4, in 
> > NameError: name 'XS' is not defined
> >
> > The problem confuse me is that is XS a local variable of the list
> > comprehension?
> 
> XS is not a local variable in the scope of either comprehension. XS is
> local to the class statement's scope. For each comprehension, an
> iterator for the current object referenced by XS gets instantiated
> (early binding) and passed as an argument to the comprehension scope.
> If we disassemble the comprehension code, we find that this iterator
> argument has the creatively illegal name ".0". (The bytecode
> references it by its fast-locals-array index, not its weird name.)
> 
> In the Z5 case, XS is a non-local variable (late binding) in the
> loop-body expression (left-hand side) of the comprehension. That's
> common for Python code. In every iteration of the loop, the
> interpreter looks up the object referenced by the name "XS", which can
> change at any time (e.g. by another thread).

In Python document 4.2.2. Resolution of names, the last paragraph:

"...A class definition is an executable statement that may use and define 
names. These references follow the normal rules for name resolution with an 
exception that unbound local variables are looked up in the global namespace. 
...The scope of names defined in a class block is limited to the class block; 
it does not extend to the code blocks of methods – this includes comprehensions 
and generator expressions since they are implemented using a function scope". 

These statements reflect the following difference:

>>> xy = [1,2]
>>> [dir() for i in xy]
[['.0', 'i'], ['.0', 'i']]
>>> [xy[0] for i in xy]
[1, 1]

>>> class foo():
... xs = [1,2]
... z4 = [dir() for i in xs]
...
>>> foo().z4
[['.0', 'i'], ['.0', 'i']]
>>> class foo():
... xs = [1,2]
... z4 = [xs[0] for i in xs]
...
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 3, in foo
  File "", line 3, in 
NameError: name 'xs' is not defined
>>>

and it goes further:

>>> [dir() for i in xy for j in xy]
[['.0', 'i', 'j'], ['.0', 'i', 'j'], ['.0', 'i', 'j'], ['.0', 'i', 'j']]
>>> class foo():
... xs = [1,2]
... z5 = [dir() for i in xs for j in xs]
...
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 3, in foo
  File "", line 3, in 
NameError: name 'xs' is not defined
>>>

That's all I had learn so far, although not understand the design decision 
behind it yet:-(

--Jach

-- 
https://mail.python.org/mailman/listinfo/python-list


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

2018-12-27 Thread Chris Angelico
On Fri, Dec 28, 2018 at 8:47 AM eryk sun  wrote:
>
> On 12/27/18, Chris Angelico  wrote:
> >
> > 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.
>
> A generator expression is implemented internally as a generator
> function that takes an iterator as its only parameter (named ".0") and
> gets called immediately to get a generator object. There's some inline
> bytecode in the defining scope that sets this up.
>
> A generator object has iterator methods (__iter__, __next__) and
> close, send, and throw methods. Its code and execution state in
> CPython uses a code object and a frame object:

A generator expression is an expression that creates and then calls a
function to create a generator object. This is exactly the same as any
other generator function being called to create a generator object.
Yes, the generator function and the generator object are very
different beasts, and yes, they're both called "a generator", and yes,
this is confusing. But it's the same whether it's a genexp or an
explicit function.

> Unlike a function object, a generator object is not a descriptor (i.e.
> it has no __get__ method) that could in principle be bound as either a
> class or instance method. Anyway, since the class doesn't exist yet,
> trying to bind and call a method at this point can't work.

This is correct. A genexp created at class scope would be
approximately equivalent to a generator function called as "Foo.f()"
not as "Foo().f()" - called on the class, not an instance of it.

> This is straying off topic, but note that a consequence of late
> binding is that super() can be broken by rebinding __class__:
>
> class C:
> def f(self):
> nonlocal __class__
> __class__ = None
> super()
>
> >>> C().f()
> Traceback (most recent call last):
>   File "", line 1, in 
>   File "", line 5, in f
> RuntimeError: super(): __class__ is not a type (NoneType)
>
> It's not a bug or design flaw; just an example of code shooting itself
> in the foot by stepping on an informally reserved dunder name.

Technically only the no-arg form of super() can be broken that way,
because it's actually a bit of magic syntax that becomes
"super(__class__, self)", except that it doesn't actually use "self"
but "whatever the first argument to this function is". You can break
it in an even simpler way:

>>> class C:
... def f():
... super()
...
>>> C.f()
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 3, in f
RuntimeError: super(): no arguments

"But Mum, I thought you said you wanted NO ARGUMENTS while you were out"

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


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

2018-12-27 Thread eryk sun
On 12/27/18, Chris Angelico  wrote:
>
> 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.

A generator expression is implemented internally as a generator
function that takes an iterator as its only parameter (named ".0") and
gets called immediately to get a generator object. There's some inline
bytecode in the defining scope that sets this up.

A generator object has iterator methods (__iter__, __next__) and
close, send, and throw methods. Its code and execution state in
CPython uses a code object and a frame object:

>>> g = (c for c in 'spam')
>>> g.gi_code.co_varnames
('.0', 'c')

Initially it hasn't run, so there's no 'c' value yet:

>>> sorted(g.gi_frame.f_locals)
['.0']

'c' is defined after it executes up to the first yield:

>>> next(g)
's'
>>> sorted(g.gi_frame.f_locals)
['.0', 'c']

Unlike a function object, a generator object is not a descriptor (i.e.
it has no __get__ method) that could in principle be bound as either a
class or instance method. Anyway, since the class doesn't exist yet,
trying to bind and call a method at this point can't work.

In contrast generator functions are commonly used for methods that can
access class attributes. But this is confusing matters since the class
__dict__ (and certainly not the instance __dict__) that's used for
attributes is not the locals() of the initial class statement
execution. In other words, class attribute access is not a closure
over the class statement scope. For methods, it depends on early
binding of the bound object to a method's __self__ attribute, which is
implicitly passed as the first argument to its __func__ function when
the method is called.

The execution locals of a class statement is a temporary namespace
that gets copied when the class object is instantiated. For example:

class C:
exec_dict = locals()

>>> C.s = 'spam'
>>> sorted(C.__dict__)
['__dict__', '__doc__', '__module__', '__weakref__', 'exec_dict', 's']

The original locals():

>>> sorted(C.exec_dict)
['__module__', '__qualname__', 'exec_dict']

What could be done is for the compiler to introduce nonlocal free
variables (like what's already done with __class__), and then capture
the current value to the locals() dict after it's done executing. For
example (a clumsy one; in practice it would be implicit, expanding on
how __class__ is implemented):

def make_Foo():
XS = None
class Foo:
nonlocal XS
XS = [15] * 4
Z5 = sum(XS[i] for i in range(len(XS)))
locals()['XS'] = XS
return Foo

>>> Foo = make_Foo()
>>> Foo.Z5
60
>>> Foo.XS
[15, 15, 15, 15]

However, this would be confusing in general, since there's no way to
keep the class attribute in sync with the cell variable. So a function
that's called as a class method may see different values for XS and
cls.XS. This is a bad idea.

This is straying off topic, but note that a consequence of late
binding is that super() can be broken by rebinding __class__:

class C:
def f(self):
nonlocal __class__
__class__ = None
super()

>>> C().f()
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 5, in f
RuntimeError: super(): __class__ is not a type (NoneType)

It's not a bug or design flaw; just an example of code shooting itself
in the foot by stepping on an informally reserved dunder name.
-- 
https://mail.python.org/mailman/listinfo/python-list


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

2018-12-27 Thread Chris Angelico
On Fri, Dec 28, 2018 at 3:31 AM Ian Kelly  wrote:
>
> On Wed, Dec 26, 2018 at 11:21 PM Chris Angelico  wrote:
> >
> > On Thu, Dec 27, 2018 at 1:56 PM  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
-- 
https://mail.python.org/mailman/listinfo/python-list


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

2018-12-27 Thread Ian Kelly
On Wed, Dec 26, 2018 at 11:21 PM Chris Angelico  wrote:
>
> On Thu, Dec 27, 2018 at 1:56 PM  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. 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.
-- 
https://mail.python.org/mailman/listinfo/python-list


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

2018-12-27 Thread eryk sun
On 12/27/18, jf...@ms4.hinet.net  wrote:
>
> 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 "", line 1, in 
>   File "", line 4, in Too
>   File "", line 4, in 
> NameError: name 'XS' is not defined
>
> The problem confuse me is that is XS a local variable of the list
> comprehension?

XS is not a local variable in the scope of either comprehension. XS is
local to the class statement's scope. For each comprehension, an
iterator for the current object referenced by XS gets instantiated
(early binding) and passed as an argument to the comprehension scope.
If we disassemble the comprehension code, we find that this iterator
argument has the creatively illegal name ".0". (The bytecode
references it by its fast-locals-array index, not its weird name.)

In the Z5 case, XS is a non-local variable (late binding) in the
loop-body expression (left-hand side) of the comprehension. That's
common for Python code. In every iteration of the loop, the
interpreter looks up the object referenced by the name "XS", which can
change at any time (e.g. by another thread).
-- 
https://mail.python.org/mailman/listinfo/python-list


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

2018-12-27 Thread Terry Reedy

On 12/26/2018 9:53 PM, jf...@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)


The iterable passed in to the comprehension is XS.


 try:
 Z5 = sum(XS[i] for i in range(len(XS)))


The iterable passed in to the comprehension function is range(len(XS)). 
XS is a class name, not a global or outer function local name, hence 
invisible to the inner function.



 except NameError:
 Z5 = None

print(Foo.Z4, Foo.Z5)

60 None



--Jach




--
Terry Jan Reedy

--
https://mail.python.org/mailman/listinfo/python-list


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

2018-12-27 Thread jfong
eryk sun於 2018年12月27日星期四 UTC+8下午2時31分58秒寫道:
> On 12/26/18, jf...@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 "", line 1, in 
> 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 "", line 1, in 
>   File "", line 1, in 
> 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 "", line 1, in 
  File "", line 4, in Too
  File "", line 4, in 
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:-(


-- 
https://mail.python.org/mailman/listinfo/python-list


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

2018-12-26 Thread eryk sun
On 12/26/18, jf...@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 "", line 1, in 
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 "", line 1, in 
  File "", line 1, in 
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']
-- 
https://mail.python.org/mailman/listinfo/python-list


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

2018-12-26 Thread Chris Angelico
On Thu, Dec 27, 2018 at 1:56 PM  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.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


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

2018-12-26 Thread Abdur-Rahmaan Janhangeer
greetings,

1)

Z4 = sum(val for val in XS)

is same as

Z4 = sum(XS)

2)

class Foo()

can also ne written as

class Foo:

3)

in Foo.x you are using the class just to assoxiate some variables with a
name. what is the purpose of tge script / what are you trying to do?

Abdur-Rahmaan Janhangeer
http://www.pythonmembers.club | https://github.com/Abdur-rahmaanJ
Mauritius
-- 
https://mail.python.org/mailman/listinfo/python-list


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

2018-12-26 Thread jfong
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


--Jach
-- 
https://mail.python.org/mailman/listinfo/python-list