# FAQ: Iterables and Iterators - Custom Iterators II

This community-built FAQ covers the “Custom Iterators II” exercise from the lesson “Iterables and Iterators”.

Paths and Courses
This exercise can be found in the following Codecademy content:

## FAQs on the exercise Custom Iterators II

There are currently no frequently asked questions associated with this exercise – that’s where you come in! You can contribute to this section by offering your own questions, answers, or clarifications on this exercise. Ask or answer a question by clicking reply () below.

If you’ve had an “aha” moment about the concepts, formatting, syntax, or anything else with this exercise, consider sharing those insights! Teaching others and answering their questions is one of the best ways to learn and stay sharp.

## Join the Discussion. Help a fellow learner on their journey.

You can also find further discussion and get answers to your questions over in #get-help.

Agree with a comment or answer? Like () to up-vote the contribution!

Need broader help or resources? Head to #get-help and #community:tips-and-resources. If you are wanting feedback or inspiration for a project, check out #project.

Looking for motivation to keep learning? Join our wider discussions in #community

Found a bug? Report it online, or post in #community:Codecademy-Bug-Reporting

Have a question about your account or billing? Reach out to our customer support team!

None of the above? Find out where to ask other questions here!

I’m stuck in an infinite loop! Here is my code:

``````class CustomerCounter:
def __iter__(self):
self.count = 0
return self

def __next__(self):
self.count += 1
return self.count
if self.count > 100:
raise StopIteration

customer_counter = CustomerCounter()
for customer in customer_counter:
print(customer)
``````

If I ran this code it takes too long to run; it’s probably stuck in an infinite loop. I decided to comment the iteration part like this:

``````class CustomerCounter:
def __iter__(self):
self.count = 0
return self

def __next__(self):
self.count += 1
return self.count
if self.count > 100:
raise StopIteration

customer_counter = CustomerCounter()
# I commented the 2 next lines because there is an infinite loop:
#for customer in customer_counter:
#print(customer)
``````

Hi! I guess that’s because Python ignores everything that comes after the return statement within a function!

What I wanted to mention is: it would be cool (sorry if that actually happens at a later point) to point out that regular (non-custom) iterator objects don’t seem to count anything but rather “eat themselves”? I mean, that is interesting and kinda unexpected behavior, right?

2 Likes

They have already implemented the `__iter__` and `__next__` methods. If you don’t want to implement that, use generator functions which will be taught after in this topic. Anyway I’m going to not change it(maybe) to stop the infinite loop because I’ve already finished this course and I’m up to the Learn Sorting Algorithms with Python course.

I don’t understand why “for customer_count in customer_counter” works when the count is set at 0.

When iterating through something like a list, there’s a set amount of data to work with and you know which one is going to be used first, next etc. But where is this when there’s only a value of 0 being assigned to a class member instead of a collection.

You might already be aware but a `for` loop effectively calls the `iter()` function on the target of the loop.

So a `for` loop is basically equivalent to a while loop of the following style-

``````it = iter(customer_counter)
while True:
try:
customer_count = next(it)
# execute statements in body of loop...
except StopIteration:
break
``````

So before the loop ever begins iteration the `iter` function is called (which makes use of the `__iter__` dunder method, setting the instance variable `.count` equal to zero). After that hopefully it’s clear how `next` simply increments this instance variable and returns the value?

Minor rant:
Personally I think the example used in this lesson breaks Python’s iterator protocol. If `CustomerCounter` itself is supposed to be an iterator then calling `iter` with this iterator object should return the exact same iterator object (it should not have side effects of resetting the iterator!).

This style can do some very alarming things with what should be an exhausted iterator (the protocol requires that once `StopIteration` is met in an iterator it only ever returns `StopIteration`). See-
https://docs.python.org/3/glossary.html#term-iterator.
https://docs.python.org/3/library/stdtypes.html#iterator-types

Once an iterator’s `__next__()` method raises `StopIteration` , it must continue to do so on subsequent calls. Implementations that do not obey this property are deemed broken.

The following displays the broken behaviour-

``````counter = CutomerCounter()
for value in counter: print(value, end=', ')  # Out: 1, 2, 3, 4, 5, 6, ...
# this iterator should be exhausted!
counter2 = iter(counter)
# note that either counter or counter2 can be used as they are the same object
for value in counter: print(value, end=', ')  # Out: 1, 2, 3, 4, 5, 6, ...
``````

If `CustomerCounter` itself is an iterator then I believe it should be written like-

``````class CustomerCounter:

def __init__(self):
self.count = 0

def __iter__(self):
return self

def __next__(self):
if self.count < 100:
self.count += 1
return self.count
else:
raise StopIteration
``````

This at least has the behaviour of a typical iterator.

Fortunately writing your own iterators is extremely uncommon since you can make use of either iterators for existing containers or if you really need customisation generators are absolutely perfect for this task.

2 Likes

Thank you! That actually makes a lot of sense. I guess it didn’t click that the value was only being initialized within the iteration protocol. Also thanks for the extra info, if i’m understanding this right, when instantiating an instance of the customer counter class once StopIteration has been reached it’ll never be able to iterate again.

1 Like

Since the `CustomerCounter` type is used as an iterator here I think that is entirely correct. There’s nothing stopping further new instances being created; you just shouldn’t reset an existing iterator object.

1 Like

Agreed! I’d love to see it revised to used best practices. I’d think at a minimum, you’d want two separate types, `Thing` and `ThingInterator`. I don’t see how an object can be its own iterator without running into problems like this… although maybe it’s possible if implemented differently?