Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #83627 > unrolled thread

Namespace puzzle, list comprehension fails within class definition

Started byJohn Ladasky <john_ladasky@sbcglobal.net>
First post2015-01-12 12:25 -0800
Last post2015-01-13 07:51 +1100
Articles 7 — 4 participants

Back to article view | Back to comp.lang.python


Contents

  Namespace puzzle, list comprehension fails within class definition John Ladasky <john_ladasky@sbcglobal.net> - 2015-01-12 12:25 -0800
    Re: Namespace puzzle, list comprehension fails within class definition Ethan Furman <ethan@stoneleaf.us> - 2015-01-12 12:40 -0800
      Re: Namespace puzzle, list comprehension fails within class definition John Ladasky <john_ladasky@sbcglobal.net> - 2015-01-12 12:49 -0800
      Re: Namespace puzzle, list comprehension fails within class definition Steven D'Aprano <steve@pearwood.info> - 2015-01-13 04:49 +0000
        Re: Namespace puzzle, list comprehension fails within class definition Ethan Furman <ethan@stoneleaf.us> - 2015-01-12 22:00 -0800
    Re: Namespace puzzle, list comprehension fails within class definition John Ladasky <john_ladasky@sbcglobal.net> - 2015-01-12 12:43 -0800
    Re: Namespace puzzle, list comprehension fails within class definition Chris Angelico <rosuav@gmail.com> - 2015-01-13 07:51 +1100

#83627 — Namespace puzzle, list comprehension fails within class definition

FromJohn Ladasky <john_ladasky@sbcglobal.net>
Date2015-01-12 12:25 -0800
SubjectNamespace puzzle, list comprehension fails within class definition
Message-ID<af9bb2fd-f0b9-4efb-874e-78f30ac65b7e@googlegroups.com>
I've never come across this before.  Here's a minimal example (in Python 3.4):


Code:
---------------------------------------------------------------------

d = {0:"a", 1:"b", 2:"c", 3:"d"}
e = [d[x] for x in (0,2)]

class Foo:
    f = {0:"a", 1:"b", 2:"c", 3:"d"}
    print(f)
    g = [f[x] for x in (0,2)]

foo = Foo()


Output:
---------------------------------------------------------------------

{0: 'a', 1: 'b', 2: 'c', 3: 'd'}

Traceback (most recent call last):
  File "minimal example.py", line 6, in <module>
    class Foo:
  File "minimal example.py", line 9, in Foo
    g = [f[x] for x in (0,2)]
  File "minimal example.py", line 9, in <listcomp>
    g = [f[x] for x in (0,2)]
NameError: name 'f' is not defined

---------------------------------------------------------------------

When I am working in the top-level namespace, I get no errors when referencing the dictionary, d, inside the list comprehension which generates e.

When I am working inside the class namespace, the print function call on line 8 recognizes the name f and prints the dictionary bound to that name.

However, the LIST COMPREHENSION defined inside the class namespace generates a NameError.

In all my years of Python programming, I guess that I have never tried to define a class attribute using a list comprehension.  Why doesn't it work?  Any advice, and suggestions for Pythonic workarounds, are appreciated.

[toc] | [next] | [standalone]


#83629

FromEthan Furman <ethan@stoneleaf.us>
Date2015-01-12 12:40 -0800
Message-ID<mailman.17638.1421095249.18130.python-list@python.org>
In reply to#83627

[Multipart message — attachments visible in raw view] — view raw

On 01/12/2015 12:25 PM, John Ladasky wrote:
> d = {0:"a", 1:"b", 2:"c", 3:"d"}
> e = [d[x] for x in (0,2)]
> 
> class Foo:
>     f = {0:"a", 1:"b", 2:"c", 3:"d"}
>     print(f)
>     g = [f[x] for x in (0,2)]

In Foo 'f' is part of an unnamed namespace; the list comp 'g' has its own namespace, effectively making be a nonlocal;
class name lookup skips nonlocal namespaces.

Workaround:  use an actual for loop.

--
~Ethan~

[toc] | [prev] | [next] | [standalone]


#83631

FromJohn Ladasky <john_ladasky@sbcglobal.net>
Date2015-01-12 12:49 -0800
Message-ID<1ec8bc0a-9391-483b-9bea-a468f97d6798@googlegroups.com>
In reply to#83629
On Monday, January 12, 2015 at 12:41:30 PM UTC-8, Ethan Furman wrote:

> In Foo 'f' is part of an unnamed namespace; the list comp 'g' has its own namespace, effectively making be a nonlocal;
> class name lookup skips nonlocal namespaces.
> 
> Workaround:  use an actual for loop.

Thanks, Ethan.  That works.

As you can see from my other post, I've just discovered that the scoping rules for list comprehensions were changed between Py2 and Py3.

[toc] | [prev] | [next] | [standalone]


#83657

FromSteven D'Aprano <steve@pearwood.info>
Date2015-01-13 04:49 +0000
Message-ID<54b4a3dd$0$2738$c3e8da3$76491128@news.astraweb.com>
In reply to#83629
On Mon, 12 Jan 2015 12:40:13 -0800, Ethan Furman wrote:

> On 01/12/2015 12:25 PM, John Ladasky wrote:
>> d = {0:"a", 1:"b", 2:"c", 3:"d"}
>> e = [d[x] for x in (0,2)]
>> 
>> class Foo:
>>     f = {0:"a", 1:"b", 2:"c", 3:"d"}
>>     print(f)
>>     g = [f[x] for x in (0,2)]
> 
> In Foo 'f' is part of an unnamed namespace; the list comp 'g' has its
> own namespace, effectively making be a nonlocal; class name lookup skips
> nonlocal namespaces.


Actually, no it doesn't.

py> def factory():
...     x = 23
...     class Inner(object):
...             print('x is', x)
...     return Inner
... 
py> o = factory()
x is 23


The "problem" is that *functions* lookup don't include the class body in 
their scope. This is by design, and goes back to Python 1.5 or older:

[steve@ando ~]$ python1.5
Python 1.5.2 (#1, Aug 27 2012, 09:09:18)  [GCC 4.1.2 20080704 (Red Hat 
4.1.2-52)] on linux2
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> class Outer:
... 	x = 23
... 	f = lambda: x+1
... 	y = f()
... 
Traceback (innermost last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 4, in Outer
  File "<stdin>", line 3, in <lambda>
NameError: x



> Workaround:  use an actual for loop.


Sad but true.



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#83664

FromEthan Furman <ethan@stoneleaf.us>
Date2015-01-12 22:00 -0800
Message-ID<mailman.17657.1421128832.18130.python-list@python.org>
In reply to#83657

[Multipart message — attachments visible in raw view] — view raw

On 01/12/2015 08:49 PM, Steven D'Aprano wrote:
> On Mon, 12 Jan 2015 12:40:13 -0800, Ethan Furman wrote:
>> 
>> [...] class name lookup skips nonlocal namespaces.
> 
> Actually, no it doesn't.
> [...] 
> The "problem" is that *functions* lookup don't include the class body in 
> their scope.

Ah, thanks for the correction!

--
~Ethan~

[toc] | [prev] | [next] | [standalone]


#83630

FromJohn Ladasky <john_ladasky@sbcglobal.net>
Date2015-01-12 12:43 -0800
Message-ID<74df17fd-b129-4207-86d8-965a93fa5ef9@googlegroups.com>
In reply to#83627
Following up to myself: I finally did the right keyword search, and found a relevant article:

http://stackoverflow.com/questions/13905741/accessing-class-variables-from-a-list-comprehension-in-the-class-definition

Maybe I HAVE tried to define a list comprehension inside a class definition before.  What I tried to do would have apparently worked in Python 2.  But in Python 3, the namespace behavior has changed (and I'm still reading the article to understand how and why).

I guess that I could define my objects as globals... now, why would Python force me into doing that? :^(

[toc] | [prev] | [next] | [standalone]


#83632

FromChris Angelico <rosuav@gmail.com>
Date2015-01-13 07:51 +1100
Message-ID<mailman.17639.1421095890.18130.python-list@python.org>
In reply to#83627
On Tue, Jan 13, 2015 at 7:25 AM, John Ladasky
<john_ladasky@sbcglobal.net> wrote:
> When I am working inside the class namespace, the print function call on line 8 recognizes the name f and prints the dictionary bound to that name.
>
> However, the LIST COMPREHENSION defined inside the class namespace generates a NameError.

A list comp is defined with a function call:

>>> def f():
...  return [x*x for x in range(4)]
...
>>> dis.dis(f)
  2           0 LOAD_CONST               1 (<code object <listcomp> at
0x7fdf25981420, file "<stdin>", line 2>)
              3 LOAD_CONST               2 ('f.<locals>.<listcomp>')
              6 MAKE_FUNCTION            0
              9 LOAD_GLOBAL              0 (range)
             12 LOAD_CONST               3 (4)
             15 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             18 GET_ITER
             19 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             22 RETURN_VALUE

This prevents leakage of the iterator into the enclosing scope.
Personally, I think it's a hack to get around the fact that Python
doesn't have any concept of sub-function-scope (similar to the weird
hack in try/except); if it weren't for that, true nesting would be
easier. As it is, function definitions in class scope have a special
meaning, and that interferes a bit with list comps.

ChrisA

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web