generator expression works in shell, NameError in script

S

ssc

Hello,

I am trying to generate this list of tuples:
[(0, ''), (1, 'Dr'), (2, 'Miss'), (3, 'Mr'), (4, 'Mrs'), (5, 'Ms')]

My code works fine in the Python shell:
titles = ['Dr', 'Miss', 'Mr', 'Mrs', 'Ms',]
title_choices = [(0, '')] + list((titles.index(t)+1, t) for t in titles)
title_choices
[(0, ''), (1, 'Dr'), (2, 'Miss'), (3, 'Mr'), (4, 'Mrs'), (5, 'Ms')]

The same code run in a script fails with
NameError: global name 'titles' is not defined

Does anybody know why ? How can I fix the error ?

Thank you very much :)

Steve
 
E

Emile van Sebille

On 6/17/2009 3:19 PM ssc said...
Hello,

I am trying to generate this list of tuples:
[(0, ''), (1, 'Dr'), (2, 'Miss'), (3, 'Mr'), (4, 'Mrs'), (5, 'Ms')]

My code works fine in the Python shell:
titles = ['Dr', 'Miss', 'Mr', 'Mrs', 'Ms',]
title_choices = [(0, '')] + list((titles.index(t)+1, t) for t in titles)
title_choices
[(0, ''), (1, 'Dr'), (2, 'Miss'), (3, 'Mr'), (4, 'Mrs'), (5, 'Ms')]

The same code run in a script fails with
NameError: global name 'titles' is not defined

You get this because titles doesn't exist in the builtin, local or
global namespaces.

Post the code that fails. It's hard to debug working code. :)

Emile
 
C

Chris Rebert

Hello,

I am trying to generate this list of tuples:
[(0, ''), (1, 'Dr'), (2, 'Miss'), (3, 'Mr'), (4, 'Mrs'), (5, 'Ms')]

My code works fine in the Python shell:
titles = ['Dr', 'Miss', 'Mr', 'Mrs', 'Ms',]
title_choices = [(0, '')] + list((titles.index(t)+1, t) for t in titles)
title_choices
[(0, ''), (1, 'Dr'), (2, 'Miss'), (3, 'Mr'), (4, 'Mrs'), (5, 'Ms')]

The same code run in a script fails with
NameError: global name 'titles' is not defined

Does anybody know why ? How can I fix the error ?

See what Emile said, but here's a nicer way to code it, IMHO:

titles = ['Dr', 'Miss', 'Mr', 'Mrs', 'Ms']
title_choices = zip(range(len(titles)+1), ['']+titles)

zip() to the rescue!

Cheers,
Chris
 
J

Jason Tackaberry

See what Emile said, but here's a nicer way to code it, IMHO:

titles = ['Dr', 'Miss', 'Mr', 'Mrs', 'Ms']
title_choices = zip(range(len(titles)+1), ['']+titles)

zip() to the rescue!

How about:

enumerate([''] + titles)
 
E

Emile van Sebille

On 6/17/2009 3:41 PM Jason Tackaberry said...
How about:

enumerate([''] + titles)

or perhaps, depending on usage...

list(enumerate(titles))

Emile
 
J

Jon Clements

Hello,

I am trying to generate this list of tuples:
[(0, ''), (1, 'Dr'), (2, 'Miss'), (3, 'Mr'), (4, 'Mrs'), (5, 'Ms')]

My code works fine in the Python shell:
titles = ['Dr', 'Miss', 'Mr', 'Mrs', 'Ms',]
title_choices = [(0, '')] + list((titles.index(t)+1, t) for t in titles)
title_choices

[(0, ''), (1, 'Dr'), (2, 'Miss'), (3, 'Mr'), (4, 'Mrs'), (5, 'Ms')]

The same code run in a script fails with
NameError: global name 'titles' is not defined

Does anybody know why ? How can I fix the error ?

Thank you very much :)

Steve

Why are you doing this? I'm assuming a code to title look up is
required (don't forget military, royal and honorable titles
etc... :) )

Why not just:
titles = ['', 'Dr', 'Miss', 'Mr', 'Mrs', 'Ms',]
lookup = dict(enumerate(titles))
print lookup
{0: '', 1: 'Dr', 2: 'Miss', 3: 'Mr', 4: 'Mrs', 5: 'Ms'}

or if you don't want a dict, then just:[(0, ''), (1, 'Dr'), (2, 'Miss'), (3, 'Mr'), (4, 'Mrs'), (5, 'Ms')]
 
S

ssc

Wow! Didn't expect that kind of instant support. Thank you very much,
I'll give both zip and enumerate a try.

The code I've shown is actually copied pretty straight from a Django
form class, but I didn't want to mention that as not to dilute the
conversation. Don't think it matters, anyway. This is the relevant
excerpt:

from django.forms import Form

class SignupForm(Form):

titles = ['Dr', 'Miss', 'Mr', 'Mrs', 'Ms',]
title_choices = [(0, '')] + list((titles.index(t)+1, t) for t in
titles)

Now that I look at it again, it seems odd to me to not have the code
e.g. in __init__(...), but just 'class-global'.
Still, that does not seem a reason for titles not to be not defined,
as I do define it just in the line above.

Does the generator expression have its own little namespace or so ?
 
C

Chris Rebert

Wow! Didn't expect that kind of instant support. Thank you very much,
I'll give both zip and enumerate a try.

The code I've shown is actually copied pretty straight from a Django
form class, but I didn't want to mention that as not to dilute the
conversation. Don't think it matters, anyway. This is the relevant
excerpt:

from django.forms import Form

class SignupForm(Form):

   titles = ['Dr', 'Miss', 'Mr', 'Mrs', 'Ms',]
   title_choices   = [(0, '')] + list((titles.index(t)+1, t) for t in
titles)

Now that I look at it again, it seems odd to me to not have the code
e.g. in __init__(...), but just 'class-global'.
Still, that does not seem a reason for titles not to be not defined,
as I do define it just in the line above.

Does the generator expression have its own little namespace or so ?

No, which leads to the common "WTF?" reaction upon seeing stuff like:
funcs = ((lambda: x) for x in range(7))
list_of = list(funcs)
list_of[0]()
6

Cheers,
Chris
 
E

Emile van Sebille

On 6/17/2009 3:54 PM ssc said...
Wow! Didn't expect that kind of instant support. Thank you very much,
I'll give both zip and enumerate a try.

The code I've shown is actually copied pretty straight from a Django
form class, but I didn't want to mention that as not to dilute the
conversation. Don't think it matters, anyway. This is the relevant
excerpt:

from django.forms import Form

class SignupForm(Form):

titles = ['Dr', 'Miss', 'Mr', 'Mrs', 'Ms',]
title_choices = [(0, '')] + list((titles.index(t)+1, t) for t in
titles)

Now that I look at it again, it seems odd to me to not have the code
e.g. in __init__(...), but just 'class-global'.

And as class is an executable statement, I imagine titles exists in a
namespace that hasn't yet been completely defined.
Still, that does not seem a reason for titles not to be not defined,
as I do define it just in the line above.

Well, again, titles will exists in the SignupForm namespace once it
exists, which it doesn't yet when you get to title_choices and want to
access it. Define titles above the class and you should be OK.
Does the generator expression have its own little namespace or so ?

Sometimes -- in some python versions generator expressions leak...

Emile
 
S

Steven Samuel Cole

Both zip and enumerate do the trick. Thanks for the pointers, that's 2
more built-ins under my belt :)
Still don't really understand why my initial code didn't work, though...

Thanks everyone! :)
 
G

guthrie

Still don't really understand why my initial code didn't work, though...

Your code certainly looks reasonable, and looks to me like it "should"
work. The comment of partial namespace is interesting, but
unconvincing (to me) - but I am not a Python expert! It would
certainly seem that within that code block it is in the local
namespace, not removed from that scope to be in another.

Seems that it should either be a bug, or else is a design error in the
language!

Just as in the above noted "WTF" - non-intuitive language constructs
that surprise users are poor design.
 
N

nn

Your code certainly looks reasonable, and looks to me like it "should"
work. The comment of partial namespace is interesting, but
unconvincing (to me) - but I am not a Python expert! It would
certainly seem that within that code block it is in the local
namespace, not removed from that scope to be in another.

Seems that it should either be a bug, or else is a design error in the
language!

Just as in the above noted "WTF" - non-intuitive language constructs
that surprise users are poor design.

This is certainly an odd one. This code works fine under 2.6 but fails
in Python 3.1.
.... lst=[2]
.... gen=[lst.index(e) for e in lst]
....
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in x
 
R

ryles

On Jun 17, 6:38 pm, Steven Samuel Cole <[email protected]>
wrote:
Your code certainly looks reasonable, and looks to me like it "should"
work. The comment of partial namespace is interesting, but
unconvincing (to me) - but I am not a Python expert! It would
certainly seem that within that code block it is in the local
namespace, not removed from that scope to be in another.
Seems that it should either be a bug, or else is a design error in the
language!
Just as in the above noted "WTF" - non-intuitive language constructs
that surprise users are poor design.

This is certainly an odd one. This code works fine under 2.6 but fails
in Python 3.1.

...     lst=[2]
...     gen=[lst.index(e) for e in lst]
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in x
  File "<stdin>", line 3, in <listcomp>
NameError: global name 'lst' is not defined

I believe it works in 2.x because unlike generator expressions, list
comprehensions do not create a new scope. However, in 3.0 list
comprehensions are actually treated as list(<generator>).

http://docs.python.org/reference/expressions.html
http://www.python.org/dev/peps/pep-0289
 
G

greg

ssc said:
class SignupForm(Form):

titles = ['Dr', 'Miss', 'Mr', 'Mrs', 'Ms',]
title_choices = [(0, '')] + list((titles.index(t)+1, t) for t in
titles)

Does the generator expression have its own little namespace or so ?

Yes. The generator expression is a function, with its
own local namespace. Since the class scope is not visible
from inside functions declared within it, the behaviour
you're seeing results.
 
G

greg

nn said:
This is certainly an odd one. This code works fine under 2.6 but fails
in Python 3.1.

... lst=[2]
... gen=[lst.index(e) for e in lst]

In 3.x it was decided that the loop variables in a list
comprehension should be local, and not leak into the
surrounding scope. This was implemented by making the
list comprehension into a nested function.

Unfortunately this leads to the same unintuitive
behaviour as a genexp when used in a class scope.
 
B

Bruno Desthuilliers

Emile van Sebille a écrit :
On 6/17/2009 3:54 PM ssc said...
Wow! Didn't expect that kind of instant support. Thank you very much,
I'll give both zip and enumerate a try.

The code I've shown is actually copied pretty straight from a Django
form class, but I didn't want to mention that as not to dilute the
conversation. Don't think it matters, anyway. This is the relevant
excerpt:

from django.forms import Form

class SignupForm(Form):

titles = ['Dr', 'Miss', 'Mr', 'Mrs', 'Ms',]
title_choices = [(0, '')] + list((titles.index(t)+1, t) for t in
titles)

Now that I look at it again, it seems odd to me to not have the code
e.g. in __init__(...), but just 'class-global'.

And as class is an executable statement, I imagine titles exists in a
namespace that hasn't yet been completely defined.
Still, that does not seem a reason for titles not to be not defined,
as I do define it just in the line above.

Well, again, titles will exists in the SignupForm namespace once it
exists, which it doesn't yet when you get to title_choices and want to
access it.

The namespace itself is as defined at any point of the class statement's
body as the local namespace of a function at any given point of the
function's body. Else decorators (or their alternate pre '@'
syntactic-sugar version) wouldn't work.


FWIW, the following code works JustFine(tm):

bruno@bruno:~$ python
Python 2.5.2 (r252:60911, Oct 5 2008, 19:24:49)
[GCC 4.3.2] on linux2.... bar = ['a', 'b', 'c']
.... baaz = list(enumerate(bar))

as well as this one:
.... bar = ['a', 'b', 'c']
.... baaz = [(bar.index(t)+1, t) for t in bar]

and this one:
.... bar = ['a', 'b', 'c']
.... baaz = list((b, b) for b in bar)



but it indeed looks like using bar.index *in a generator expression*
fails (at least in 2.5.2) :
.... bar = ['a', 'b', 'c']
.... baaz = list((bar.index(b), b) for b in bar)
....
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in Foo
File "<stdin>", line 3, in <genexpr>
NameError: global name 'bar' is not defined


Looks like a bug to me :-/
 
M

Mark Dickinson

[...]
but it indeed looks like using bar.index *in a generator expression*
fails (at least in 2.5.2) :

  >>> class Foo(object):
...     bar = ['a', 'b', 'c']
...     baaz = list((bar.index(b), b) for b in bar)
...
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
   File "<stdin>", line 3, in Foo
   File "<stdin>", line 3, in <genexpr>
NameError: global name 'bar' is not defined

Looks like a bug to me :-/

I think it's working as intended: name resolution for
nested scopes skips intermediate class scopes. The
'Discussion' section of PEP 227 (Statically Nested Scopes)
is illuminating; here's a snippet:

"""Names in class scope are not accessible. Names are resolved in
the innermost enclosing function scope. If a class definition
occurs in a chain of nested scopes, the resolution process skips
class definitions. This rule prevents odd interactions between
class attributes and local variable access."""

Mark
 
G

greg

ryles said:
However, in 3.0 list
comprehensions are actually treated as list(<generator>).

That's not quite true -- it would be rather inefficient
if it was. The code generated for the LC is much the same
as it was in 2.x. But the whole LC is put into a nested
function so that the loop variables are local to it.
 
G

guthrie

nn said:
This is certainly an odd one. This code works fine under 2.6 but fails
in Python 3.1.
...     lst=[2]
...     gen=[lst.index(e) for e in lst]

In 3.x it was decided that the loop variables in a list
comprehension should be local, and not leak into the
surrounding scope. This was implemented by making the
list comprehension into a nested function.

Unfortunately this leads to the same unintuitive
behaviour as a genexp when used in a class scope.
-- I don't get this - the only local loop variable is "e", not lst.
And lst is used twice in the generator expression, which is being
complained about?

It would generally be the case that a nested function would have
access to its enclosing scope.

In any case, even if/when the current behavior is explained or
rationalized, it certainly appears un-intuitive, and that is another
level of "error"! :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,078
Latest member
MakersCBDBlood

Latest Threads

Top