human
I am working on the file "016 - Yak Shaving/func_py/func_py.ipynb".
The first task is:
Here, x is the iterable, while y and z are two individual instances of an iterator, producing values from the iterable x. Both y and z hold state, as you can see from the example. In this example, x is a data structure (a list), but that is not a requirement.
Often, for pragmatic reasons, iterable classes will implement both __iter__() and __next__() in the same class, and have __iter__() return self, which makes the class both an iterable and its own iterator. It is perfectly fine to return a different object as the iterator, though.

When you write:
python
x = [1, 2, 3]
for elem in x:
    ...
This is what actually happens:
<img src="figures/iterable_vs_iterator.png" style="display:block;margin:auto;width:80%;"/>
Can you write Python program for it?
assistant
#Here actually happens:
.

s = 'cat'  # s is an ITERABLE
           # s is a str object that is immutable
           # s has no state
           # s has a __getitem__() method 

t = iter(s)    # t is an ITERATOR
               # t has state (it starts by pointing at the "c")
               # t has a __next__() method and an __iter__() method
try:
    print(next(t))        # the next() function returns the next value and advances the state
    print(next(t))        # the next() function returns the next value and advances
    print(next(t))        # the next() function returns the next value and advances
    print(next(t))        # next() raises StopIteration to signal that iteration is complete
except StopIteration as e:
    print("StopIteration raised")

class FibonacciIterator:
    """
    Produces an arbitrary number of the Fibonacci numbers.
    Is an both an iterable and an iterator.
    """
    def __init__(self):
        self.prev = 0
        self.curr = 1
    
    def __iter__(self):
        return self
    
    def __next__(self):
        self.prev, self.curr = self.curr, self.prev + self.curr
        return self.prev

class Countdown:
    """A simple iterable, NOT an iterator"""
    def __iter__(self):
        return iter([5, 4, 3, 2, 1, 'launch'])
    
f = FibonacciIterator()
print([next(f) for _i in range(10)])
c = Countdown()
print([i for i in c])