I\'m trying to figure out what is the pythonic way to unpack an iterator inside of a list.
For example:
my_iterator = zip([1, 2, 3, 4], [1, 2, 3, 4]
If you're interested in the least amount of typing possible, you can actually do one character better than my_list = [*my_iterator]
with iterable unpacking:
*my_list, = my_iterator
or (although this only equals my_list = [*my_iterator]
in the number of characters):
[*my_list] = my_iterator
(Funny how it has the same effect as my_list = [*my_iterator]
.)
For the most Pythonic solution, however, my_list = list(my_iterator)
is clearly the clearest and the most readable of all, and should therefore be considered the most Pythonic.
I tend to use zip if I need to convert a list to a dictionary or use it as a key-value pair in a loop or list comprehension.
However, if this is only for illustration to create an iterator. I will definitely vote for #3 for clarity.
After exploring more the subject I've come with some conclusions.
There should be one-- and preferably only one --obvious way to do it
(zen of python)
Deciding which option is the "pythonic" one should take into consideration some criteria :
And the obvious "pythonic" option winning in all criteria is option number 3):
list = list(my_iterator)
Here is why is "obvious" that no 3) is the pythonic one:
option 1) (unpacking using *) asterisk operator can be a bit confusing if you don't use it regularly, there are 4 cases for using the asterisk in Python:
Another good argument is python docs themselves, I have done some statistics to check which options are chosen by the docs, for this I've chose 4 buil-in iterators and everything from the module itertools (that are used like: itertools.
) to see how they are unpacked in a list:
After exploring the docs I found: 0 iterators unpacked in a list using option 1) and 2) and 35 using option 3).
Conclusion :
The pythonic way to unpack an iterator inside of a list is:
my_list = list(my_iterator)
This might be a repeat of Fastest way to convert an iterator to a list, but your question is a bit different since you ask which is the most Pythonic. The accepted answer is list(my_iterator)
over [e for e in my_iterator]
because the prior runs in C under the hood. One commenter suggests [*my_iterator]
is faster than list(my_iterator)
, so you might want to test that. My general vote is that they are all equally Pythonic, so I'd go with the faster of the two for your use case. It's also possible that the older answer is out of date.
While the unpacking operator *
is not often used for unpacking a single iterable into a list (therefore [*it]
is a bit less readable than list(it)
), it is handy and more Pythonic in several other cases:
mixed_list = [a, *it, b]
This is more concise and efficient than
mixed_list = [a]
mixed_list.extend(it)
mixed_list.append(b)
mixed_list = [*it1, *it2, a, b, ... ]
This is similar to the first case.
first, *rest = it
This extracts the first element of it
into first
and unpacks the rest into a list. One can even do
_, *mid, last = it
This dumps the first element of it
into a don't-care variable _
, saves last element into last
, and unpacks the rest into a list mid
.
it = (0, range(5), 3)
a1, (*a2,), a3 = it # Unpack the second element of it into a list a2
e1, (first, *rest), e3 = it # Separate the first element from the rest while unpacking it[1]
This can also be used in for
statements:
from itertools import groupby
s = "Axyz123Bcba345D"
for k, (first, *rest) in groupby(s, key=str.isalpha):
...