Is there any practical difference between list(iterable)
and [*iterable]
in versions of Python that support the latter?

- 38,994
- 14
- 83
- 119

- 2,811
- 22
- 26
-
7Corner case: it's possible to rebind the name `list` to something other than the built-in type, but you can't change the meaning of the `[*iterable]` syntax. – chepner Sep 27 '18 at 14:39
5 Answers
list(x)
is a function, [*x]
is an expression. You can reassign list
, and make it do something else (but you shouldn't).
Talking about cPython, b = list(a)
translates to this sequence of bytecodes:
LOAD_NAME 1 (list)
LOAD_NAME 0 (a)
CALL_FUNCTION 1
STORE_NAME 2 (b)
Instead, c = [*a]
becomes:
LOAD_NAME 0 (a)
BUILD_LIST_UNPACK 1
STORE_NAME 3 (c)
so you can argue that [*a]
might be slightly more efficient, but marginally so.

- 32,265
- 7
- 79
- 80
You can use the standard library module dis
to investigate the byte code generated by a function. In this case:
import dis
def call_list(x):
return list(x)
def unpacking(x):
return [*x]
dis.dis(call_list)
# 2 0 LOAD_GLOBAL 0 (list)
# 2 LOAD_FAST 0 (x)
# 4 CALL_FUNCTION 1
# 6 RETURN_VALUE
dis.dis(unpacking)
# 2 0 LOAD_FAST 0 (x)
# 2 BUILD_LIST_UNPACK 1
# 4 RETURN_VALUE
So there is a difference and it is not only the loading of the globally defined name list
, which does not need to happen with the unpacking. So it boils down to how the built-in list
function is defined and what exactly BUILD_LIST_UNPACK
does.
Note that both are actually a lot less code than writing a standard list comprehension for this:
def list_comp(x):
return [a for a in x]
dis.dis(list_comp)
# 2 0 LOAD_CONST 1 (<code object <listcomp> at 0x7f65356198a0, file "<ipython-input-46-dd71fb182ec7>", line 2>)
# 2 LOAD_CONST 2 ('list_comp.<locals>.<listcomp>')
# 4 MAKE_FUNCTION 0
# 6 LOAD_FAST 0 (x)
# 8 GET_ITER
# 10 CALL_FUNCTION 1
# 12 RETURN_VALUE

- 6,891
- 27
- 47
Since [*iterable]
is unpacking, it accepts assignment-like syntax, unlike list(iterable)
:
>>> [*[]] = []
>>> list([]) = []
File "<stdin>", line 1
SyntaxError: can't assign to function call
You can read more about this here (not useful though).
You can also use list(sequence=iterable)
, i.e. with a key-word argument:
>>> list(sequence=[])
[]
Again not useful.

- 38,994
- 14
- 83
- 119
-
While one could write `[*var] = iterable` to mean `var = list(iterable)`, from readability standpoint it’s too much even to someone who’s no stranger to code golf. Also what an interesting symmetry between `[*var] = iterable` and `var = [*iterable]` (though broken in case of `(*var,) = iterable` and `var = (*iterable,)`). – Roman Odaisky Sep 27 '18 at 15:10
-
@RomanOdaisky Indeed, `*lst, = (1,2,2); print(lst)` being the simplest I guess, though still not for recommended use – Chris_Rands Sep 27 '18 at 15:27
-
Actually, I found a valid use case for this: `for keys, [*rows] in itertools.groupby(...):`! – Roman Odaisky Nov 28 '18 at 17:51
There's always going to be some differences between two constructs that do the same thing. Thing is, I wouldn't say the differences in this case are actually practical. Both are expressions that take the iterable, iterate through it and then create a list out of it.
The contract is the same: input is an iterable output is a list populated by the iterables elements.
Yes, list
can be rebound to a different name; list(it)
is a function call while [*it]
is a list display; [*it]
is faster with smaller iterables but generally performs the same with larger ones. Heck, one could even throw in the fact that [*it]
is three less keystrokes.
Are these practical though? Would I think of them when trying to get a list out of an iterable? Well, maybe the keystrokes in order to stay under 79 characters and get the linter to shut it up.

- 150,925
- 31
- 268
- 253
Apparently there’s a performance difference in CPython, where [*a] overallocates and list() doesn’t: What causes [*a] to overallocate?

- 2,811
- 22
- 26