Python Forum
Unpacking zip object - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: General Coding Help (https://python-forum.io/forum-8.html)
+--- Thread: Unpacking zip object (/thread-36737.html)

Pages: 1 2


Unpacking zip object - Mark17 - Mar-24-2022

Here's some code:

exceptions = 0

a = ['1-6-2017', '1-13-2017', '1-20-2017', '1-27-2017']
b = [265, -10, 130, 330]
c = ['d', '', 'd', '']

try:
    xp, yp, m = zip(a,b,c)
except:
    output = zip(a, b, c)
    exceptions +=1

for xp, yp, m in output:
    print(xp, yp, m)
    
print(f'Number of exceptions is {exceptions}.')
Why do I need the for loop to unpack this? Without it, the ValueError is "too many items to unpack" and I don't fully understand how that arises.

From the try block, I would expect to see a list of tuples.


RE: Unpacking zip object - bowlofred - Mar-24-2022

Zip returns an iterator. You can loop over that iterator to get the elements, or you can assign all the elements.

But since you hand your zip iterables that all have 4 elements, your zip iterator will also have 4 elements.
This means if you try to assign it to (xp, yp, m), it will complain that 4 elements can't fit into 3 variables. I can't tell from your program if you mean for xp to have the first "set" from the zip or if you expected it to have the "a" portion.

zip([1,2,3,4], [5,6,7,8]) => (1,5), (2,6), (3,7), (4,8) # 4 elements.

Describe more what you want those variables to contain, but looping over a zip is pretty natural.


RE: Unpacking zip object - Coricoco_fr - Mar-24-2022

Hello,
Sorry


RE: Unpacking zip object - Mark17 - Mar-24-2022

(Mar-24-2022, 07:41 PM)bowlofred Wrote: Zip returns an iterator. You can loop over that iterator to get the elements, or you can assign all the elements.

But since you hand your zip iterables that all have 4 elements, your zip iterator will also have 4 elements.
This means if you try to assign it to (xp, yp, m), it will complain that 4 elements can't fit into 3 variables. I can't tell from your program if you mean for xp to have the first "set" from the zip or if you expected it to have the "a" portion.

zip([1,2,3,4], [5,6,7,8]) => (1,5), (2,6), (3,7), (4,8) # 4 elements.

Describe more what you want those variables to contain, but looping over a zip is pretty natural.

I'm still confused.

My thinking was that this would unpack the lists one element at a time and put the a component in xp, the b component in yp, and the c component in m. There would be 4 groupings of 3 elements, but I thought number of groupings would be inconsequential as any could be accommodated. What would be problematic would be trying to unpack 4 variables to 3 or 5 to 3 or any unequal number. I have three (a, b, c) being unpacked to three (xp, yp, m).


RE: Unpacking zip object - bowlofred - Mar-24-2022

(Mar-24-2022, 09:32 PM)Mark17 Wrote: I have three (a, b, c) being unpacked to three (xp, yp, m).

But you don't have 3 elements in a,b,c, you have a total of 12. You can't put them into 3 variables with a single assignment. You could do that if you loop over them and change it each time.

Is this what you intend?

a = ['1-6-2017', '1-13-2017', '1-20-2017', '1-27-2017']
b = [265, -10, 130, 330]
c = ['d', '', 'd', '']


for xp, yp, m in zip(a, b, c):
    print(f"This time through the loop {xp=}, {yp=}, {m=}")
Output:
This time through the loop xp='1-6-2017', yp=265, m='d' This time through the loop xp='1-13-2017', yp=-10, m='' This time through the loop xp='1-20-2017', yp=130, m='d' This time through the loop xp='1-27-2017', yp=330, m=''



RE: Unpacking zip object - Mark17 - Mar-25-2022

(Mar-24-2022, 10:09 PM)bowlofred Wrote:
(Mar-24-2022, 09:32 PM)Mark17 Wrote: I have three (a, b, c) being unpacked to three (xp, yp, m).

But you don't have 3 elements in a,b,c, you have a total of 12. You can't put them into 3 variables with a single assignment. You could do that if you loop over them and change it each time.

Is this what you intend?

a = ['1-6-2017', '1-13-2017', '1-20-2017', '1-27-2017']
b = [265, -10, 130, 330]
c = ['d', '', 'd', '']


for xp, yp, m in zip(a, b, c):
    print(f"This time through the loop {xp=}, {yp=}, {m=}")
Output:
This time through the loop xp='1-6-2017', yp=265, m='d' This time through the loop xp='1-13-2017', yp=-10, m='' This time through the loop xp='1-20-2017', yp=130, m='d' This time through the loop xp='1-27-2017', yp=330, m=''

Ah ha!! That's what I did not understand. It's all clear now. Thanks!


RE: Unpacking zip object - deanhystad - Mar-25-2022

I can't tell if this is a confusion about iterators or a confusion about unpacking question.

As bowlofred said, zip returns an iterator. You give it an iterable. It returns an iterator. Each time you call next(iterator) you get the next item in the iterable.
a = ['1-6-2017', '1-13-2017', '1-20-2017', '1-27-2017']
x = zip(a)
print(next(x), next(x))
Output:
('1-6-2017',) ('1-13-2017',)
Notice that zip returns tuples. This is because zip expects you to provide multiple iterables.
a = ['1-6-2017', '1-13-2017', '1-20-2017', '1-27-2017']
b = [265, -10, 130, 330]
c = ['d', '', 'd', '']
x = zip(a, b, c)
print(next(x), next(x))
Output:
('1-6-2017', 265, 'd') ('1-13-2017', -10, '')
If you pass zip 1 iterable, each tuple contains 1 value. If you pass zip 3 iterables, each tuple contains 3 values.

Since zip is an iterator, it will return all the values one at a time and then raise a StopIteration exception.
a = ['1-6-2017', '1-13-2017', '1-20-2017', '1-27-2017']
b = [265, -10, 130, 330]
c = ['d', '', 'd', '']
x = zip(a, b, c)
print(next(x), next(x), next(x), next(x), next(x))
Error:
Traceback (most recent call last): File "...", line 5, in <module> print(next(x), next(x), next(x), next(x), next(x)) StopIteration
Normally you don't have to worry about the StopIteration exception because you use zip() in a for loop, and "for" processes the exception as the end of loop condition.

a, b, and c each have 4 items, so zip(a, b, c) can be iterated 4 times before raising the StopIteration exception.

Python supports packing and unpacking sequences. An iterator is a sequence, so you can unpack an iterator.
a = ['1-6-2017', '1-13-2017', '1-20-2017', '1-27-2017']
b = [265, -10, 130, 330]
c = ['d', '', 'd', '']
x, y, *z = zip(a, b, c)
print(x, y, z)
Output:
('1-6-2017', 265, 'd') ('1-13-2017', -10, '') [('1-20-2017', 130, 'd'), ('1-27-2017', 330, '')]
I told Python that z will take all remaining values from the iterator, and when printing you can see that z contains two values, not one. This makes sense. a, b and c each are each len 4. zip(a, b, c) will produce 4 values before raising the StopIteration exception. Unpacking puts the first item in x, the second in y, and the two remaining items in z. There are 4 items to unpack. 4 items cannot be unpacked into 3 variables (well, they can, but you know what I mean).

When I first read you post I thought you were trying to transpose this group of lists:
Quote:a = ['1-6-2017', '1-13-2017', '1-20-2017', '1-27-2017']
b = [265, -10, 130, 330]
c = ['d', '', 'd', '']
Into somethin like this:
Quote:[('1-6-2017', 265, 'd'), ('1-13-2017', -10, ''), ('1-20-2017', 130, 'd'), ('1-27-2017', 330, '')]
The Python for doing that is:
x = list(zip(a, b, c))
And I could then iterate over the tuples in x.
for xp, yp, m in x:
    print(xp, yp, m)
Output:
1-6-2017 265 d 1-13-2017 -10 1-20-2017 130 d 1-27-2017 330
Now I am unpacking tuples, not an iterator. Each tuple is len 3, so I need 3 variables to unpack.


RE: Unpacking zip object - Mark17 - Mar-26-2022

(Mar-25-2022, 07:27 PM)deanhystad Wrote: I can't tell if this is a confusion about iterators or a confusion about unpacking question.

As bowlofred said, zip returns an iterator. You give it an iterable. It returns an iterator. Each time you call next(iterator) you get the next item in the iterable.
...
Python supports packing and unpacking sequences.  An iterator is a sequence, so you can unpack an iterator.
[python]a = ['1-6-2017', '1-13-2017', '1-20-2017', '1-27-2017']
b = [265, -10, 130, 330]
c = ['d', '', 'd', '']
x, y, *z = zip(a, b, c)
print(x, y, z)
Output:
('1-6-2017', 265, 'd') ('1-13-2017', -10, '') [('1-20-2017', 130, 'd'), ('1-27-2017', 330, '')]
I told Python that z will take all remaining values from the iterator, and when printing you can see that z contains two values, not one. This makes sense. a, b and c each are each len 4. zip(a, b, c) will produce 4 values before raising the StopIteration exception. Unpacking puts the first item in x, the second in y, and the two remaining items in z. There are 4 items to unpack. 4 items cannot be unpacked into 3 variables (well, they can, but you know what I mean).

...

What I took from bowlofred was the "too many items to unpack" Traceback referred to a total of 12 items trying to be unpacked into 3 variables. What you're saying here, though, is that it's about four sets being unpacked into three variables. I have misunderstood on this point. I have expected values from a to go in xp, values from b to go in yp, and values from c to go in m. I now believe it's the first grouping of x, y, z going into xp, the second grouping of x, y, z going into yp, and the remaining (with the * operator) groupings going into m. I'll have to go back to bowlofred's comments and see how they compare to the example you gave.

The question here that pertains most to my current application is why does the first ax.plot() output a line while the second ax.plot outputs a set of markers (or lack thereof if I indicate None):

a = ['1-6-2017', '1-13-2017', '1-20-2017', '1-27-2017']
b = [265, -10, 130, 330]
c = ['d', '', 'd', '']

fig, ax = plt.subplots(1)

ax.plot(a,b) #This plots line.
plt.xticks(rotation=90) 
for xp, yp, m in zip(a, b, c): #need for-loop (else "too many values to unpack")
    ax.plot(xp,yp,m,color='orange',markersize=12) #this plots markers
plt.tight_layout() #this doesn't seem to do anything?
plt.show()
I'm going to take a guess. ax.plot() is a method that plots line graphs. However, if you present just one value at a time, then it's only going to plot the point. The second line is a loop that revisits ax.plot() several times but each time, presents only one set of values to it.


RE: Unpacking zip object - deanhystad - Mar-26-2022

Matplotlib draws lines between points. If you give it 100 points it will draw 99 lines. If you give it two points it will draw 1 line. If you give it one point it doesn't draw a line.

Try passing some arguments to tight_layout. The effects can be subtle. It is more noticeable if you have multiple subplots.


RE: Unpacking zip object - deanhystad - Mar-26-2022

What you were missing is the intermediate step int he packing/unpacking.
zip(a, b, c) produces this series:
('1-6-2017', 265, 'd'), ('1-13-2017', -10, ''), ('1-20-2017', 130, 'd'), ('1-27-2017', 330, '')
or
(a0, b0, c0), (a1, b1, c1), (a2, b2, c2), (a3, b3, c3)

When you do xp, yp, m = zip(a, b, c) it tries to unpack that series of 4 into 3 variables. 4 does not fit into 3, so you get an error.

When you do "for xp, yp, m in zip(a, b, c): you are unpacking the tuple (ax, bx, cx) into xp, yp and m.