Hone logo
Hone
Problems

Custom List Iterator

Iterators are a fundamental concept in Python, enabling sequential access to elements of a collection. Understanding and implementing iterators is crucial for writing efficient and Pythonic code. This challenge asks you to create your own iterable list-like object and implement its iterator.

Problem Description

You need to create a Python class named MyList that mimics the behavior of a standard Python list, but with a custom iterator. This MyList class should store a collection of elements (which can be of any type) and provide a way to iterate over them one by one.

Your task involves two main parts:

  1. Implementing the MyList class:

    • The MyList class should accept an iterable (like a list, tuple, or another MyList instance) during initialization and store its elements internally.
    • It should support basic list operations like len() and accessing elements by index (e.g., my_list[i]).
    • Crucially, it must implement the __iter__ method, which should return an iterator object.
  2. Implementing the iterator class:

    • You need to create a separate class, let's call it MyListIterator, which will be responsible for the actual iteration logic.
    • This MyListIterator class must implement the iterator protocol, which consists of two methods:
      • __iter__: This method should return the iterator object itself (i.e., self).
      • __next__: This method should return the next item in the sequence. When there are no more items, it should raise a StopIteration exception.

Expected Behavior:

Your MyList object, when used in a for loop or with iter() and next(), should yield its elements in the order they were provided during initialization.

Edge Cases:

  • An empty MyList should be handled correctly (i.e., iterating over it should immediately raise StopIteration).
  • The iterator should work correctly even if the MyList is modified after the iterator has been created (though this is an advanced consideration and might not be strictly required if you assume immutability during iteration for this challenge).

Examples

Example 1:

# Initialization of MyList
items = [1, 2, 3, 4, 5]
my_list = MyList(items)

# Iteration using a for loop
print("Iterating using for loop:")
for item in my_list:
    print(item)

# Output:
# Iterating using for loop:
# 1
# 2
# 3
# 4
# 5

Explanation: The for loop implicitly calls iter(my_list) to get an iterator, and then repeatedly calls next() on that iterator. Our MyList and MyListIterator classes should facilitate this behavior.

Example 2:

# Initialization of MyList with different data types
mixed_items = ["apple", 10, True, 3.14]
my_list_mixed = MyList(mixed_items)

# Iteration using iter() and next()
print("\nIterating using iter() and next():")
iterator = iter(my_list_mixed)
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))

# Attempting to get the next item after exhaustion
try:
    print(next(iterator))
except StopIteration:
    print("StopIteration caught as expected.")

# Output:
# Iterating using iter() and next():
# apple
# 10
# True
# 3.14
# StopIteration caught as expected.

Explanation: This example demonstrates how to manually use the iterator protocol with iter() and next(), and shows the expected StopIteration behavior when the iterator is exhausted.

Example 3:

# Initialization with an empty list
empty_list = MyList([])

# Iteration over an empty list
print("\nIterating over an empty list:")
try:
    for item in empty_list:
        print(item)
except StopIteration:
    print("No items to iterate over.")

# Output:
# Iterating over an empty list:
# No items to iterate over.

Explanation: This tests the edge case of an empty MyList. The for loop should not execute its body and should effectively terminate immediately.

Constraints

  • The internal storage for MyList can be a standard Python list.
  • The MyList class should handle any number of elements up to a reasonable limit for memory.
  • The MyListIterator should maintain its internal state correctly to track the current position.
  • Performance is not a primary concern for this challenge, but your solution should be functional and correct.

Notes

  • Recall that any object that implements the __iter__ method is called an "iterable".
  • Any object that implements both __iter__ and __next__ is called an "iterator".
  • The __iter__ method of an iterable should return an iterator object.
  • The __iter__ method of an iterator should return self.
  • Consider how you will keep track of the current position within the MyList's elements in your MyListIterator class. An index is a common approach.
  • You can optionally implement __len__ and __getitem__ on MyList for better compatibility with list-like behavior, although the core of the challenge is the iterator implementation.
Loading editor...
python