Skip to content

Instantly share code, notes, and snippets.

@mportesdev
Last active September 11, 2020 07:47
Show Gist options
  • Save mportesdev/5f89c56dc5d97a6269c8edea682f9c5a to your computer and use it in GitHub Desktop.
Save mportesdev/5f89c56dc5d97a6269c8edea682f9c5a to your computer and use it in GitHub Desktop.
Python 3.8 assignment expressions – first encounter

Python 3.8 vs PEP 572

According to PEP 572, section Scope of the target, the following examples of assignment expressions should be invalid and raise TargetScopeError.

Example 1

[i := i+1 for i in range(5)]

Python 3.8.0a1 (tags/v3.8.0a1:e75eeb00b5, Feb  3 2019, 20:47:39) [MSC v.1916 64 bit (AMD64)] on win32
>>> test = [i := i+1 for i in range(5)]
>>> i
5
>>> test
[1, 2, 3, 4, 5]
>>>

No error here. Works as expected, equivalent to

>>> test = []
>>> for i in range(5):
...     i = i + 1
...     test.append(i)
...
>>> i
5
>>> test
[1, 2, 3, 4, 5]
>>>

Example 2

[[(j := j) for i in range(5)] for j in range(5)]

Python 3.8.0a1 (tags/v3.8.0a1:e75eeb00b5, Feb  3 2019, 20:47:39) [MSC v.1916 64 bit (AMD64)] on win32
>>> test = [[(j := j) for i in range(5)] for j in range(5)]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <listcomp>
  File "<stdin>", line 1, in <listcomp>
NameError: name 'j' is not defined
>>>

I don't understand this error. This listcomp should be equivalent to

>>> test = []
>>> for j in range(5):
...     sublist = []
...     for i in range(5):
...         j = j
...         sublist.append(j)
...     test.append(sublist)
...
>>> j
4
>>> test
[[0, 0, 0, 0, 0], [1, 1, 1, 1, 1], [2, 2, 2, 2, 2], [3, 3, 3, 3, 3], [4, 4, 4, 4, 4]]
>>>

Or, according to PEP 572, Appendix B, equivalent to

>>> def f():
...     if False:
...         j = None
...     def outer_generator(outer_iterator):
...         nonlocal j
...         def inner_generator(inner_iterator):
...             nonlocal j
...             for i in inner_iterator:
...                 j = j
...                 yield j
...         for j in outer_iterator:
...             yield list(inner_generator(range(5)))
...     test = list(outer_generator(range(5)))
...     print(j)
...     print(test)
...
>>> f()
4
[[0, 0, 0, 0, 0], [1, 1, 1, 1, 1], [2, 2, 2, 2, 2], [3, 3, 3, 3, 3], [4, 4, 4, 4, 4]]
>>>

Example 3

[i := 0 for i, j in stuff]

Python 3.8.0a1 (tags/v3.8.0a1:e75eeb00b5, Feb  3 2019, 20:47:39) [MSC v.1916 64 bit (AMD64)] on win32
>>> stuff = [(11, 22), (33, 44)]
>>> test = [i := 0 for i, j in stuff]
>>> i
0
>>> test
[0, 0]
>>>

No error here. Works as expected, equivalent to

>>> stuff = [(11, 22), (33, 44)]
>>> test = []
>>> for i, j in stuff:
...     i = 0
...     test.append(i)
...
>>> i
0
>>> test
[0, 0]
>>>

Example 4

[i+1 for i in i := stuff]

Python 3.8.0a1 (tags/v3.8.0a1:e75eeb00b5, Feb  3 2019, 20:47:39) [MSC v.1916 64 bit (AMD64)] on win32
>>> stuff = [11, 22, 33, 44]
>>> test = [i+1 for i in i := stuff]
  File "<stdin>", line 1
    test = [i+1 for i in i := stuff]
                           ^
SyntaxError: invalid syntax

>>> test = [i+1 for i in (i := stuff)]
>>> i
[11, 22, 33, 44]
>>> test
[12, 23, 34, 45]
>>>

Obvious syntax error (in has higher priority than :=). After adding parentheses, the listcomp works as expected, equivalent to

>>> stuff = [11, 22, 33, 44]
>>> test = []
>>> i = stuff
>>> for i in i:
...     test.append(i+1)
...
>>> i
44
>>> test
[12, 23, 34, 45]
>>>

(except that in the latter case, the loop target i is not hidden in an implicit inner scope)

Update 26 Feb 2019:

The above tested behavior is the same in version 3.8.0a2.

Update 1 Apr 2019:

The above tested behavior is the same in version 3.8.0a3.

Update 17 May 2019:

The above tested behavior is the same in version 3.8.0a4.

Update 5 Jun 2019:

The above tested behavior is the same in version 3.8.0b1.

Update 11 Jul 2019:

The above tested behavior is the same in version 3.8.0b2.

Update 8 Aug 2019:

The above tested behavior is the same in version 3.8.0b3.

Update 18 Sep 2019:

The above tested behavior changed in version 3.8.0b4. All the example comprehensions now raise SyntaxError:

Python 3.8.0b4 (tags/v3.8.0b4:d93605d, Aug 29 2019, 23:21:28) [MSC v.1916 64 bit (AMD64)] on win32
>>> test = [i := i+1 for i in range(5)]
  File "<stdin>", line 1
SyntaxError: assignment expression cannot rebind comprehension iteration variable 'i'
>>>
Python 3.8.0b4 (tags/v3.8.0b4:d93605d, Aug 29 2019, 23:21:28) [MSC v.1916 64 bit (AMD64)] on win32
>>> test = [[(j := j) for i in range(5)] for j in range(5)]
  File "<stdin>", line 1
SyntaxError: assignment expression cannot rebind comprehension iteration variable 'j'
>>>
Python 3.8.0b4 (tags/v3.8.0b4:d93605d, Aug 29 2019, 23:21:28) [MSC v.1916 64 bit (AMD64)] on win32
>>> stuff = [(11, 22), (33, 44)]
>>> test = [i := 0 for i, j in stuff]
  File "<stdin>", line 1
SyntaxError: assignment expression cannot rebind comprehension iteration variable 'i'
>>>
Python 3.8.0b4 (tags/v3.8.0b4:d93605d, Aug 29 2019, 23:21:28) [MSC v.1916 64 bit (AMD64)] on win32
>>> stuff = [11, 22, 33, 44]
>>> test = [i+1 for i in (i := stuff)]
  File "<stdin>", line 1
SyntaxError: assignment expression cannot be used in a comprehension iterable expression
>>>
Update 2 Oct 2019:

No change between version 3.8.0b4 and 3.8.0rc1.

Update 21 Oct 2019:

No change between version 3.8.0rc1 and 3.8.0.

Note:

No TargetScopeError or any other new exception class has been added in Python 3.8.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment