How would I modify an outside variable within a list comprehension?

61 views Asked by At

Python code:

i: int = 1
table: list = [[i, bit, None, None] for bit in h]

Expected behaviour: i needs to be incremented by 1 per iteration.

Pseudocode:

i: int = 1
table: list = [[i++, bit, None, None] for bit in h]
3

There are 3 answers

0
Mark Tolonen On BEST ANSWER

List comprehensions shouldn't be used for outside side-effects. In this case, use enumerate instead of an outside variable:

h = '10010010'  # Undefined in OP example.  Just something to enumerate.
table = [[i, bit, None, None ] for i, bit in enumerate(h, start=1)]
for row in table:
    print(row)

Output:

[1, '1', None, None]
[2, '0', None, None]
[3, '0', None, None]
[4, '1', None, None]
[5, '0', None, None]
[6, '0', None, None]
[7, '1', None, None]
[8, '0', None, None]
1
Georgina Skibinski On

As a thought experiment that's the literal answer to your question:

>>> i=0
>>> [[i:=i+1, bit, None, None] for bit in h]
[[1, '1', None, None], [2, '0', None, None], [3, '0', None, None], [4, '1', None, None], [5, '0', None, None], [6, '0', None, None], [7, '1', None, None], [8, '0', None, None]]

you definitely shouldn't be doing that, and go with enumerate instead per accepted answer

0
no comment On

More ways to get the result:

[[i, bit, None, None]
 for i in [0]
 for bit in h
 for i in [i+1]]

[a[:]
 for a in [[None] * 4]
 for a[:2] in enumerate(h, start=1)]

[[*tpl]
 for nones in [repeat(None)]
 for tpl in zip(count(1), h, nones, nones)]

[*map(list, zip(count(1), h, nones := repeat(None), nones))]

Benchmark, can't currently run it :-(

import timeit

setup = '''
from itertools import count, repeat
h = '10010010'
'''

codes = [
    'lst = [[i, bit, None, None ] for i, bit in enumerate(h, start=1)]',
    'i=0; lst = [[i:=i+1, bit, None, None] for bit in h]',
    'lst = [[i, bit, None, None] for i in [0] for bit in h for i in [i+1]]',
    'lst = [a[:] for a in [[None] * 4] for a[:2] in enumerate(h, start=1)]',
    'lst = [[*tpl] for nones in [repeat(None)] for tpl in zip(count(1), h, nones, nones)]',
    'lst = [*map(list, zip(count(1), h, nones := repeat(None), nones))]',
]

exec(setup)
for code in codes:
    exec(code)
    print(lst)
    del lst

setup = '''
from itertools import count, repeat
h = '10010010' * 1000
'''

for code in codes:
    t = min(timeit.repeat(code, setup, number=10, repeat=25)) / 10
    print(f'{t * 1e3:4.2f} ms ', code)

Attempt This Online!