PEP 572: Add Appendix B with translations of various comprehension cases (#700)
This commit is contained in:
parent
52113333bd
commit
97ff8893b0
131
pep-0572.rst
131
pep-0572.rst
|
@ -259,7 +259,8 @@ this special case may be removed from the specification of assignment
|
|||
expressions. Note that the problem already exists for *using* a
|
||||
variable defined in the class scope from a comprehension.)
|
||||
|
||||
TODO: nested comprehensions
|
||||
See Appendix B for some examples of how the rules for targets in
|
||||
comprehensions translate to equivalent code.
|
||||
|
||||
TODO: use a a subclass of SyntaxError for various prohibitions?
|
||||
|
||||
|
@ -1027,6 +1028,134 @@ To my eyes, the original form is harder to understand::
|
|||
return a
|
||||
|
||||
|
||||
Appendix B: Rough code translations for comprehensions
|
||||
======================================================
|
||||
|
||||
This appendix attempts to clarify (though not specify) the rules when
|
||||
a target occurs in a comprehension or in a generator expression.
|
||||
For a number of illustrative examples we show the original code,
|
||||
containing a comprehension, and the translation, where the
|
||||
comprehension has been replaced by an equivalent generator function
|
||||
plus some scaffolding.
|
||||
|
||||
Since ``[x for ...]`` is equivalent to ``list(x for ...)`` these
|
||||
examples all use list comprehensions without loss of generality.
|
||||
And since these examples are meant to clarify edge cases of the rules,
|
||||
they aren't trying to look like real code.
|
||||
|
||||
Note: comprehensions are already implemented via synthesizing nested
|
||||
generator functions like those in this appendix. The new part is
|
||||
adding appropriate declarations to establish the intended scope of
|
||||
assignment expression targets (the same scope they resolve to as if
|
||||
the assignment were performed in the block containing the outermost
|
||||
comprehension). For type inference purposes, these illustrative
|
||||
expansions do not imply that assignment expression targets are always
|
||||
Optional (but they do indicate the target binding scope).
|
||||
|
||||
Let's start with a reminder of what code is generated for a generator
|
||||
expression without assignment expression.
|
||||
|
||||
- Original code (EXPR usually references VAR)::
|
||||
|
||||
def f():
|
||||
a = [EXPR for VAR in ITERABLE]
|
||||
|
||||
- Translation (let's not worry about name conflicts)::
|
||||
|
||||
def f():
|
||||
def genexpr(iterator):
|
||||
for VAR in iterator:
|
||||
yield EXPR
|
||||
a = list(genexpr(iter(ITERABLE)))
|
||||
|
||||
Let's add a simple assignment expression.
|
||||
|
||||
- Original code::
|
||||
|
||||
def f():
|
||||
a = [TARGET := EXPR for VAR in ITERABLE]
|
||||
|
||||
- Translation::
|
||||
|
||||
def f():
|
||||
if False:
|
||||
TARGET = None # Dead code to ensure TARGET is a local variable
|
||||
def genexpr(iterator):
|
||||
nonlocal TARGET
|
||||
for VAR in iterator:
|
||||
TARGET = EXPR
|
||||
yield TARGET
|
||||
a = list(genexpr(iter(ITERABLE)))
|
||||
|
||||
Let's add a ``global TARGET`` declaration in ``f()``.
|
||||
|
||||
- Original code::
|
||||
|
||||
def f():
|
||||
global TARGET
|
||||
a = [TARGET := EXPR for VAR in ITERABLE]
|
||||
|
||||
- Translation::
|
||||
|
||||
def f():
|
||||
global TARGET
|
||||
def genexpr(iterator):
|
||||
global TARGET
|
||||
for VAR in iterator:
|
||||
TARGET = EXPR
|
||||
yield TARGET
|
||||
a = list(genexpr(iter(ITERABLE)))
|
||||
|
||||
Or instead let's add a ``nonlocal TARGET`` declaration in ``f()``.
|
||||
|
||||
- Original code::
|
||||
|
||||
def g():
|
||||
TARGET = ...
|
||||
def f():
|
||||
nonlocal TARGET
|
||||
a = [TARGET := EXPR for VAR in ITERABLE]
|
||||
|
||||
- Translation::
|
||||
|
||||
def g():
|
||||
TARGET = ...
|
||||
def f():
|
||||
nonlocal TARGET
|
||||
def genexpr(iterator):
|
||||
nonlocal TARGET
|
||||
for VAR in iterator:
|
||||
TARGET = EXPR
|
||||
yield TARGET
|
||||
a = list(genexpr(iter(ITERABLE)))
|
||||
|
||||
Finally, let's nest two comprehensions.
|
||||
|
||||
- Original code::
|
||||
|
||||
def f():
|
||||
a = [[TARGET := i for i in range(3)] for j in range(2)]
|
||||
# I.e., a = [[0, 1, 2], [0, 1, 2]]
|
||||
print(TARGET) # prints 2
|
||||
|
||||
- Translation::
|
||||
|
||||
def f():
|
||||
if False:
|
||||
TARGET = None
|
||||
def outer_genexpr(outer_iterator):
|
||||
nonlocal TARGET
|
||||
def inner_generator(inner_iterator):
|
||||
nonlocal TARGET
|
||||
for i in inner_iterator:
|
||||
TARGET = i
|
||||
yield i
|
||||
for j in outer_iterator:
|
||||
yield list(inner_generator(range(3)))
|
||||
a = list(outer_genexpr(range(2)))
|
||||
print(TARGET)
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
|
|
Loading…
Reference in New Issue