I'm currently trying to learn more about for loops and generators, and I came across this piece of code:
class OdeStore:
def __init__(self,data):
self.data=data #list of form [[t0,u0],[t1,u1]...]
def __iter__(self):
for t,u in self.data: #specify the data iterated over
yield u #how it is yielded
store=OdeStore([[0,1],[0.1,1.1],[0.2,1.3]])
for u in store:
print(u)
If my understanding is correct, we may define the __iter__()
inside a class to define how a for loop will act when it involves an instance of the class. In this case, we state that once we loop over an instance of the OdeStore class, the second entry of the nested lists will be yielded. Is my interpretation of the code correct? My confusion mainly comes from including a for loop inside the definition of __iter__
.
For example if we now apply the list() function, we obtain:
list(store) # returns [1, 1.1, 1.3]
I've tried to find how the list() function operates, but I suppose it appends entries of an object to an empty list (or something thereof).
Perhaps inserting some print statements will provide more information:
class OdeStore:
def __init__(self,data):
self.data=data #list of form [[t0,u0],[t1,u1]...]
def __iter__(self):
print(" starting __iter__")
for t,u in self.data: #specify the data iterated over
print(f" iter at {t},{u}, returning {u}")
yield u #how it is yielded
print("iterating over OdeStore")
store=OdeStore([[0,1],[0.1,1.1],[0.2,1.3]])
for u in store:
print(u)
print("starting list")
l = list(store)
print(f"list: {l}")
Output:
iterating over OdeStore
starting __iter__
iter at 0,1, returning 1
1
iter at 0.1,1.1, returning 1.1
1.1
iter at 0.2,1.3, returning 1.3
1.3
starting list
starting __iter__
iter at 0,1, returning 1
iter at 0.1,1.1, returning 1.1
iter at 0.2,1.3, returning 1.3
list: [1, 1.1, 1.3]
You can see the initial call to __iter__
(made when the for loop starts) and that the for loop inside __iter__
is "paused" by the yield call. Conceptually, the state of execution is saved by the yield call, a value is returned to the for loop, and the next time the for loop calls in to the iterator execution is resumed with the saved context.
I've tried to find how the list() function operates, but I suppose it appends entries of an object to an empty list (or something thereof).
I believe this is correct on a conceptual level. As you can see from the output, during list construction an iterator is created and walks the OdeStore.
Maybe breaking it down this way will make it a little more concrete. Let's change our script to this:
class OdeStore:
def __init__(self,data):
self.data=data #list of form [[t0,u0],[t1,u1]...]
def __iter__(self):
print(" starting __iter__")
for index in range(0, len(self.data)):
print(f" iter at index {index}, returning {self.data[index][1]}")
yield self.data[index][1]
#for t,u in self.data: #specify the data iterated over
# print(f" iter at {t},{u}, returning {u}")
# yield u #how it is yielded
store=OdeStore([[0,1],[0.1,1.1],[0.2,1.3]])
print("Creating iterator")
iter = iter(store)
print("calling next 1st time")
print(next(iter))
print("calling next 2nd time")
print(next(iter))
print("calling next 3rd time")
print(next(iter))
print("calling next 4th time")
print(next(iter))
Output:
Creating iterator
calling next 1st time
starting __iter__
iter at index 0, returning 1
1
calling next 2nd time
iter at index 1, returning 1.1
1.1
calling next 3rd time
iter at index 2, returning 1.3
1.3
calling next 4th time
Traceback (most recent call last):
File "./odestore.py", line 25, in <module>
print(next(iter))
StopIteration
Conceptually, every yield
is saving off the index and returning the calculated value. When the iterator is invoked again, this stored index is incremented (because of the for
loop in __iter__
). The call to yield saves of the new value of index
and returns the calculated value. When we run out of values, __iter__
returns instead of yielding and the iterator implementation class (called generator
) raises the StopIteration
exception.
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments