简体   繁体   English

根据 class 理解 __iter__() 方法

[英]Understanding the __iter__() method in terms of a class

I'm currently trying to learn more about for loops and generators, and I came across this piece of code:我目前正在尝试了解有关 for 循环和生成器的更多信息,并且遇到了这段代码:

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.如果我的理解是正确的,我们可以在__iter__()来定义当涉及 class 的实例时 for 循环将如何作用。 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.在这种情况下,我们 state ,一旦我们遍历 OdeStore class 的实例,将产生嵌套列表的第二个条目。 Is my interpretation of the code correct?我对代码的解释是否正确? My confusion mainly comes from including a for loop inside the definition of __iter__ .我的困惑主要来自于在__iter__的定义中包含一个 for 循环。

For example if we now apply the list() function, we obtain:例如,如果我们现在应用 list() function,我们得到:

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).我试图找出 list() function 是如何运作的,但我想它将 object 的条目附加到一个空列表(或其中的一些)。

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: 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.您可以看到对__iter__的初始调用(在 for 循环开始时进行)并且__iter__内的 for 循环被 yield 调用“暂停”。 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.从概念上讲,执行的 state 由 yield 调用保存,将一个值返回给 for 循环,并在下一次 for 循环调用迭代器时使用保存的上下文继续执行。

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).我试图找出 list() function 是如何运作的,但我想它将 object 的条目附加到一个空列表(或其中的一些)。

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.从 output 可以看出,在列表构建期间,会创建一个迭代器并遍历 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: 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.从概念上讲,每个yield都在节省指数并返回计算值。 When the iterator is invoked again, this stored index is incremented (because of the for loop in __iter__ ).当再次调用迭代器时,这个存储的索引会增加(因为__iter__中的for循环)。 The call to yield saves of the new value of index and returns the calculated value.调用 yield 保存index的新值并返回计算值。 When we run out of values, __iter__ returns instead of yielding and the iterator implementation class (called generator ) raises the StopIteration exception.当我们用完值时, __iter__返回而不是产生,并且迭代器实现 class (称为generator )引发StopIteration异常。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM