The Python Oracle

"Tuple comprehensions" and the star splat/unpack operator *

--------------------------------------------------
Hire the world's top talent on demand or became one of them at Toptal: https://topt.al/25cXVn
--------------------------------------------------

Music by Eric Matyas
https://www.soundimage.org
Track title: Ancient Construction

--

Chapters
00:00 &Quot;Tuple Comprehensions&Quot; And The Star Splat/Unpack Operator *
01:30 Accepted Answer Score 1
02:39 Thank you

--

Full question
https://stackoverflow.com/questions/4757...

--

Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...

--

Tags
#python #python3x #tuples #listcomprehension #generatorexpression

#avk47



ACCEPTED ANSWER

Score 1


To me, it seems like the second example is also one where a generator object is created first. Is this correct?

Yes, you're correct, checkout the CPython bytecode:

>>> import dis
>>> dis.dis("*(thing for thing in thing),")
  1           0 LOAD_CONST               0 (<code object <genexpr> at 0x7f56e9347ed0, file "<dis>", line 1>)
              2 LOAD_CONST               1 ('<genexpr>')
              4 MAKE_FUNCTION            0
              6 LOAD_NAME                0 (thing)
              8 GET_ITER
             10 CALL_FUNCTION            1
             12 BUILD_TUPLE_UNPACK       1
             14 POP_TOP
             16 LOAD_CONST               2 (None)
             18 RETURN_VALUE

Is there any difference between these expressions in terms of what goes on behind the scenes? In terms of performance? I assume the first and third could have latency issues while the second could have memory issues (as is discussed in the linked comments).

My timings suggest the first 1 is slightly faster, presumably because the unpacking is more expensive via BUILD_TUPLE_UNPACK than the tuple() call:

>>> from timeit import timeit
>>> def f1(): tuple(thing for thing in range(100000))
... 
>>> def f2(): *(thing for thing in range(100000)),
... 
>>> timeit(lambda: f1(), number=100)
0.5535585517063737
>>> timeit(lambda: f2(), number=100)
0.6043887557461858

Comparing the first one and the last, which one is more pythonic?

The first one seems far more readable to me, and also will work across different Python versions.