iefgnoix
iefgnoix

Reputation: 91

Confusion about one example to check if an object is an iterator in Python

I have read some posts on stackoverflow about how to check an object is an iterator in Python but it seems that they did not solve my question. I have this example from the book Effective Python

def normalize_defensive(numbers):
   if iter(numbers) is iter(numbers):  # An iterator — bad!
       raise TypeError(‘Must supply a container’)
   total = sum(numbers)
   result = []
   for value in numbers:
       percent = 100 * value / total
       result.append(percent)
   return result

To use:

visits = [15, 35, 80]
normalize_defensive(visits)  # No error
visits = ReadVisits(path)  # ReadVisits is a class with an __iter__ method.
normalize_defensive(visits)  # No error
it = iter(visits)
normalize_defensive(it)
>>>
TypeError: Must supply a container

So my question is in this line:

if iter(numbers) is iter(numbers):  # An iterator — bad!

Why this line check if the variable numbers is an iterator? When visits = [15, 35, 80], should it be true that iter(numbers) is iter(numbers)?

Upvotes: 0

Views: 198

Answers (2)

Martijn Pieters
Martijn Pieters

Reputation: 1121914

When you call an iterable, calling iter() on such an object will always produce a new iterator object. But calling iter() on an iterator will always return the same object; it is a requirement of the iterator protocol that they do.

From the iterator.__iter__() documentation:

Return the iterator object itself. This is required to allow both containers and iterators to be used with the for and in statements.

Because iter(iterable) always returns itself, the test iter(obj) is iter(obj) will be true; the same object is returned in both cases.

Upvotes: 2

Moses Koledoye
Moses Koledoye

Reputation: 78556

To help you understand Martijn's explanation, take a look at the following:

>>> numbers = [15, 35, 80]
>>> it  = iter(numbers)
>>> it2 = iter(numbers)
>>> it3 = iter(it)
>>> id(it1)
51123792
>>> id(it2)
51056464    # id of it2 is different from it1
>>> id(it3) 
51123792    # iterator of iterator it3 has the same id as iterator it1

So if numbers was an iterator, calling iter on numbers will always return objects that are internally identical: iter(numbers) is iter(numbers) will be True.

Upvotes: 0

Related Questions