Understanding the __iter__() method in terms of a class

William

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).

DraftyHat

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.

edited at
0

Comments

0 comments
Login to comment

Related

Class generator without the __iter__() method but object instance works with next()

Yielding a dictionary key, value pair in __iter__ method of a class

Python 3: How to write a __iter__ method for derived class so that it extends on the behaviour of the base class' __iter__ method

__iter__ in Class file, for loop

Why is iter not a method of an instance and __iter__ is?

What is the use of returning self in the __iter__ method?

Defining `__iter__` method for a dask actor?

How to override __iter__() for custom class?

Do Python iterators formally require an __iter__ method?

Why does calling list() on an instance with an __iter__() method cause recursion?

How to loop through parent class using __iter__ and yield?

Understanding Static Class and static method

Understanding method / class calls in Java

Confused on understanding the cryptographic terms

Understanding types and terms in Haskell

Why does mypy not consider a class as iterable if it has __len__ and __getitem__ but no __iter__

How the iter() method work in the str class?

Trouble understanding the calling of global variables with in a class to a method within the class

Understanding pointers in C in terms of strings

struggling in understanding terms in the Delegate world

Use with in __iter__

Class terms in Dart

Need help in understanding the reason for the use of the setRootPaneCheckingEnabled() method of the JFrame class

Trouble understanding private attributes in classes and the class property method in Python 3

print list / help understanding class vs driver method placement

Understanding Class<?>

Understanding docker swarm in terms of high availability

Understanding terms for h-index calculation

Understanding Theano Example in terms of GPU cores/threads