doesn’t work, and I don’t understand why
The zipped object has no length. So, you cast it as a list. However, as I understand it, it’s not really a list. It’s a list of tuples. And I’m thinking that you can’t use list methods on a zipped object.
See:
https://docs.python.org/3/library/functions.html#zip
You can do this with a list:
names = ["Mohamed", "Sara", "Xia", "Paul", "Valentina", "Jide", "Aaron", "Emily", "Nikita", "Paul"]
occurrences_p = names.count("Paul")
print(occurrences_p)
>>2
But, once the zipped object is cast as a list the object is consumed which is why .count()
doesn’t work.
So you would need to write a for
loop to count the number of times Paul occurs.
Or, you could do something kind of ugly like this (but I wouldn’t):
names = ["Mohamed", "Sara", "Xia", "Paul", "Valentina", "Jide", "Aaron", "Emily", "Nikita", "Paul"]
insurance_costs = [13262.0, 4816.0, 6839.0, 5054.0, 14724.0, 5360.0, 7640.0, 6072.0, 2750.0, 12064.0]
mylist = [names, insurance_costs]
[item.count("Paul") for item in mylist]
>>>[2, 0]
This is really a job for @mtf . And I’m going to learn something today.
Let’s examine list
objects:
a = ['one', 'two', 'three', 'four', 'five']
b = [1, 2, 3, 4, 5]
>>> len(a); len(b)
5
5
Both objects have measurable length.
>>> hasattr(a, 'count')
True
>>> hasattr(b, 'count')
True
>>>
Both objects have a ‘count’ attribute.
>>> hasattr(a, '__iter__')
True
>>> hasattr(b, '__iter__')
True
>>>
So we can iterate the lists.
>>> hasattr(a, '__next__')
False
>>> hasattr(b, '__next__')
False
>>>
list
objects do not have a __next__
attribute, so they are not iterators.
Now let’s create a zip
object:
c = zip(a, b)
>>> len(c)
Traceback (most recent call last):
File "<pyshell#25>", line 1, in <module>
len(c)
TypeError: object of type 'zip' has no len()
>>> hasattr(c, 'count')
False
>>>
Iterators do not have a ‘count’ attribute since they have no len()
.
>>> hasattr(c, '__iter__')
True
>>>
That means we can iterate an iterator (in this case, a zip object).
>>> for a, b in c:
print (a, b)
one 1
two 2
three 3
four 4
five 5
>>>
Iterables do not have a __next__
attribute, but iterators do:
>>> hasattr(a, '__next__')
False
>>> hasattr(b, '__next__')
False
>>> hasattr(c, '__next__'`)
True
>>> next(c)
Traceback (most recent call last):
File "<pyshell#13>", line 1, in <module>
next(c)
StopIteration
>>>
That tells us that we consumed the iterator when we iterated it. To reuse it, we need to recreate it.
c = zip(a, b)
Let’s deploy the __next__
attribute on the zip object:
>>> while True:
... print (next(c))
...
...
('one', 1)
('two', 2)
('three', 3)
('four', 4)
('five', 5)
Traceback (most recent call last):
File "<pyshell#31>", line 2, in <module>
print (next(c))
StopIteration
>>>
Again, we will have consumed the interator so must recreate it again if we wish to further examine the object.
Now we know that an interator has no ‘count’ attribute and no len()
so how do we measure its size? Short answer, apart from consuming it and counting as we go, there is no way. We have to consume it.
Casting an iterator to an iterable is probably the simplest way to ensure it has both len()
and a ‘count’ attribute. This will, you guessed it, consume the iterator.
d = list(c)
>>> len(d)
5
>>> hasattr(d, 'count')
True
>>> print (d)
[('one', 1), ('two', 2), ('three', 3), ('four', 4), ('five', 5)]
How do we know that the iterator is consumed? Simple, try its next()
:
>>> next(c)
Traceback (most recent call last):
File "<pyshell#13>", line 1, in <module>
next(c)
StopIteration
>>>
Same as above.
We have seen that iterators are iterable, but they get consumed. The advantage to this is that no matter how many values are queued up to be iterated, the object has the exact same size. It only knows where the NEXT
one is, since it is queued up. This means we don’t have to occupy a lot of memory with another larger list than the two separate ones that already exist, and we can still iterate both lists at the same time.
What’s more, we can zip any number of lists into one iterator so essentially iterate any number of lists at the same time, and all while using no memory, apart from the small footprint of the zip object.
You see where this is going, eh? In a one-off situation we can spin out an interator, run our process over it, and move on without leaving a huge footprint in memory. Note that the list we cast from the zip is taking up memory like any list would.
This has rather rambled on, so I’ll stop here and let you take this in.