I'm trying to understand what is happening:
a = list('hello world')
b = [a[i::l]for i in range(8)]
I would then expect that:
zip(*b) == a
However what I get is the following:
[('h', 'e', 'l', 'l', 'o', ' ', 'w', 'o')]
Maybe it's a failing on my part to understand what zip(*) doe but I thought it unpacks a list of list and makes a single list out of it. Where am I going wrong?
You missed a detail specific to zip()
, as outlined in the documentation :
The iterator stops when the shortest input iterable is exhausted
hello world
has 11 characters in it, a prime number, so apart from a list of 11 separate sequences with each one character, there is no way to produce a list of lists without at least one of those being shorter.
For example, if we presume that l = 8
(anything 5 and over would produce the output you've shown), then a
is set to:
[['h', 'r'], ['e', 'l'], ['l', 'd'], ['l'], ['o'], [' '], ['w'], ['o']]
That's 8 lists, with the first containing 2 elements, the remainder have just one. So only the first elements of these are then used to produce combinations:
>>> [l[0] for l in b]
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o']
You only looped 8 times, so there only 8 top-level lists in b
to take letters from. For different values of l
of 5 or up you'll get a different distribution of the remaining letters but with only 3 more characters remaining there aren't many ways of distributing those remaining letters across the lists, and with l
below 8
, you just add repeated letters (as [0::l]
and [7::l]
are guaranteed to overlap for any l
equal to 7 or lower).
You'd have to loop up to 11 times and take every 11th character to get something that'll zip to the same sequence:
>>> b = [a[i::11]for i in range(11)]
>>> b
[['h'], ['e'], ['l'], ['l'], ['o'], [' '], ['w'], ['o'], ['r'], ['l'], ['d']]
>>> list(zip(*b))
[('h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd')]
This still isn't the same as a
, because zip()
produces sequence of sequences (here it's just one because there is only a single value in each nested list). You could use next()
to get the first (and only) element:
>>> a == list(next(zip(*b)))
True
You can instead use itertools.zip_longest()
to continue iteration until the longest input iterable is exhausted, and add a default filler value to augment the shorter sequences. An empty string would work if you wanted to join the sequences back to whole strings again:
try:
# Python 3
from itertools import zip_longest
except ImportError:
# Python 2 has the same object, but with i prefixed
from itertools import izip_longest as zip_longest
result = list(zip_longest(*b, fillvalue=''))
This however produces two tuples; there are two columns in the input, after all:
>>> from itertools import zip_longest
>>> b = [a[i::8]for i in range(8)]
>>> list(zip_longest(*b, fillvalue=''))
[('h', 'e', 'l', 'l', 'o', ' ', 'w', 'o'), ('r', 'l', 'd', '', '', '', '', '')]
You'd have to chain them to re-combine them; itertools.chain.from_iterable()
could do that:
>>> from itertools import chain
>>> ''.join(chain.from_iterable(zip_longest(*b, fillvalue='')))
'hello world'
This only works for l = 8
, again, because of overlapping slices for lower values of l
, For l > 8
, you start out missing characters from the end as none of the 8 a[i::l]
slices include those characters
>>> for l in range(2, 12):
... print(f'{l:>2d}:', ''.join(chain.from_iterable(zip_longest(*[a[i::l] for i in range(8)], fillvalue=''))))
...
2: hello wollo worlo worldworldrldd
3: hello wolo worldworldld
4: hello woo worldrld
5: hello wo worldd
6: hello woworld
7: hello woorld
8: hello world
9: hello wold
10: hello wod
11: hello wo
Your code isn't so clear, and we don't know what the l
really is! if u run it as it is, you will surely get an error saying that the l isn't defined.
However, for the zip function it stops at the shortest iterator, to force it keep going you should use zip_longest
.
for more details on how zip function works check this : Python zip
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.