HER*_*ERO 7 python oop iterator
我正在读一本关于 Python 的书,其中说明了如何实现迭代器协议。
class Fibbs:
def __init__(self):
self.a = 0
self.b = 1
def __next__(self):
self.a, self.b = self.b, self.a + self.b
return self.a
def __iter__(self):
return self
Run Code Online (Sandbox Code Playgroud)
在这里,self它本身就是可迭代和迭代器,我相信?然而,下面的段落说:
请注意,迭代器实现了该
__iter__方法,实际上该方法将返回迭代器本身。在许多情况下,您可以将该__iter__方法放入另一个对象中,并在 for 循环中使用该对象。然后将返回你的迭代器。建议迭代器__iter__另外实现自己的方法(返回 self,就像我在这里所做的那样),这样它们本身就可以直接在 for 循环中使用。
这是否意味着您可以将__iter__()和放入__next__()两个不同的对象中?可以对属于不同类的对象执行此操作吗?只能对属于不同类的对象执行此操作吗?这可能是实现迭代器协议的有点奇怪的方式。但我只是想看看如何实现,前提是它实际上可以这样实现。
Sha*_*ger 18
有两种方法可以做到这一点:
__iter__为return self,仅__next__在同一个类上实现。您已经编写了一个迭代器。__iter__返回一些遵循 #1 规则的其他对象(一种便宜的方法是将其编写为生成器函数,这样您就不必手动实现其他类)。不执行__next__. 您编写了一个不是迭代器的迭代器。对于每个协议的正确实现版本,区分它们的方式是方法__iter__。如果主体只是return self(可能带有日志语句或其他内容,但没有其他副作用),那么它要么是一个迭代器,要么写得不正确。如果主体是其他任何东西,那么它要么是非迭代器可迭代,要么写得不正确。任何其他行为都违反了协议的要求。
在情况 #2 中,根据定义,另一个对象将属于另一个类(因为您要么具有幂等性__iter__并实现__next__,要么只有__iter__, without __next__,这会生成一个新的迭代器)。
您甚至需要迭代器的原因__iter__是支持以下模式:
iterable = MyIterable(...)
iterator = iter(iterable) # Invokes MyIterable.__iter__
next(iterator, None) # Throw away first item
for x in iterator: # for implicitly calls iterator's __iter__; dies if you don't provide __iter__
Run Code Online (Sandbox Code Playgroud)
总是为可迭代对象返回一个新的迭代器,而不是仅仅使它们成为迭代器并在__iter__调用时重置状态的原因是为了处理上述情况(如果MyIterable只是返回自身并重置迭代,则for循环的隐式调用__iter__将再次重置它并撤消第一个元素的预期跳过)并支持如下模式:
for x in iterable:
for y in iterable: # Operating over product of all elements in iterable
Run Code Online (Sandbox Code Playgroud)
如果__iter__将自身重置为开始并且只有一个状态,则将:
xiterable重置,然后迭代将每个值放入的整个过程yx它也是必需的,因为 Python 假定这iter(x) is x是一种安全、无副作用的方法来测试可迭代对象是否是迭代器。如果你__iter__修改了自己的状态,那么它并不是没有副作用的。最坏的情况是,对于可迭代对象,它应该浪费一点时间来创建一个立即被丢弃的迭代器。对于迭代器来说,它实际上应该是免费的(因为它只是返回自身)。
这是否意味着您可以将
__iter__()和放入__next__()两个不同的对象中?
对于迭代器,你不能(它必须有两种方法,尽管__iter__很简单)。对于非迭代器iterable s,您必须(它必须仅具有__iter__, 并返回一些其他迭代器对象)。没有“可以”。
可以对属于不同类的对象执行此操作吗?
是的。
只能对属于不同类的对象执行此操作吗?
是的。
可迭代的示例:
class MyRange:
def __init__(self, start, stop):
self.start = start
self.stop = stop
def __iter__(self):
return MyRangeIterator(self) # Returns new iterator, as this is a non-iterator iterable
# Likely to have other methods (because iterables are often collections of
# some sort and support many other behaviors)
# Does *not* have __next__, as this is not an iterator
Run Code Online (Sandbox Code Playgroud)
迭代器的示例:
class MyRangeIterator: # Class is often non-public and or defined inside the iterable as
# nested class; it exists solely to store state for iterator
def __init__(self, rangeobj): # Constructed from iterable; could pass raw values if you preferred
self.current = rangeobj.start
self.stop = rangeobj.stop
def __iter__(self):
return self # Returns self, because this is an iterator
def __next__(self): # Has __next__ because this is an iterator
retval = self.current # Must cache current because we need to modify it before we return
if retval >= self.stop:
raise StopIteration # Indicates iterator exhausted
self.current += 1 # Ensure state updated for next call
return retval # Return cached value
# Unlikely to have other methods; iterators are generally iterated and that's it
Run Code Online (Sandbox Code Playgroud)
“简单迭代”的示例,其中您不通过创建__iter__生成器函数来实现自己的迭代器类:
class MyEasyRange:
def __init__(self, start, stop): ... # Same as for MyRange
def __iter__(self): # Generator function is simpler (and faster)
# than writing your own iterator class
current = self.start # Can't mutate attributes, because multiple iterators might rely on this one iterable
while current < self.stop:
yield current # Produces value and freezes generator until iteration resumes
current += 1
# reaching the end of the function acts as implicit StopIteration for a generator
Run Code Online (Sandbox Code Playgroud)