New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[doc] Unintuitive error when using generator expression in class property #71138
Comments
Why should I be able to access B but not C? |
Using a simple metaclass shows that definition order is not at fault here: >>> class PrintOrder(dict):
... def __setitem__(self, item, value):
... print(item, value)
... super().__setitem__(item, value)
...
>>> class Show(type):
... def __prepare__(name, bases): return PrintOrder()
...
>>> class A(metaclass=Show):
... B = range(10)
... C = frozenset([4, 5, 6])
... D = list(i for i in B)
... E = list(i for i in B if i in C)
...
__module__ __main__
__qualname__ A
B range(0, 10)
C frozenset({4, 5, 6})
D [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in A
File "<stdin>", line 5, in <genexpr>
NameError: name 'C' is not defined However, the following works: >>> def local_definition():
... F = frozenset([4, 5, 6])
... class A(metaclass=Show):
... B = range(10)
... C = frozenset([4, 5, 6])
... D = list(i for i in B)
... E = list(i for i in B if i in F)
... return A
...
>>> local_definition()
__module__ __main__
__qualname__ local_definition.<locals>.A
B range(0, 10)
C frozenset({4, 5, 6})
D [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
E [4, 5, 6]
<class '__main__.local_definition.<locals>.A'> Sounds like either an inconsistency between 'for' and 'if' parts of generator expressions, or something weird about the way generator expressions work that I'm unaware of. Furthermore, this isn't documented in the tutorial, the functional programming How-To, the 2.4 release notes or the PEP-289: https://docs.python.org/3/tutorial/classes.html#generator-expressions |
On snap! Nicely found! This seems to have something to do with the way generator expressions are run inside their own scope. In Python 2.7, a list comprehension in a class sees the locals correctly: py> class K: But change the list comp to a generator expression, and the situation is different: py> class K:
... a = 2; x = list(i for i in range(a)) # Okay
... b = 2; y = list(b+i for i in range(a)) # Raises
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in K
File "<stdin>", line 3, in <genexpr>
NameError: global name 'b' is not defined In Python 3, list comps use the same sort of temporary scope as genexprs, and sure enough, the list comp fails with the same error as the generator expression. |
The outer for loop in a generator expression is evaluated immediately; everything after that is evaluated lazily. This was a deliberate design choice: see https://www.python.org/dev/peps/pep-0289/#early-binding-versus-late-binding for some rationale. This explains why you're able to access "B". The inability to access "C" is due to the usual rule for Python scope resolution: class scopes are skipped when examining nested scopes. The early evaluation of the outermost for is covered in the docs, here: https://docs.python.org/2/reference/expressions.html#generator-expressions """ So this behaviour is by design, though there may be a doc issue here. Changing components and versions accordingly. |
See also bpo-3692. |
Hello,
I don't think it is a doc bug. It belongs to interpreter-core. Best regards, |
The following equivalent code works :
It clearly shows that the implicit promise that dict comprehension is an equivalent way to replace a for loop is broken. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: