【发布时间】:2021-10-15 07:46:52
【问题描述】:
我在阅读 What exactly are iterator, iterable, and iteration? 和 Build a basic Python iterator 时意识到在实践中我不明白必须如何实现可迭代类。
假设我有以下课程:
class MyClass():
def __init__(self, num):
self.num = num
self.count = 0
def __len__(self):
return self.num
def __iter__(self):
return self
def __next__(self):
if self.count < self.num:
v = self.count
self.count += 1
return v
else:
self.count = 0
raise StopIteration
该类是可迭代的,因为它“有一个返回迭代器的__iter__ 方法”*1。 MyClass 的对象也是迭代器,因为“迭代器是具有 next (Python 2) 或 __next__ (Python 3) 方法的对象。”*1.到目前为止一切顺利。
让我困惑的是一条评论说“迭代器只应该被迭代一次”*2。我不明白为什么以下 sn-p 会永远卡住:
>>> y = MyClass(5)
>>> print([[i for i in y] for i in y])
当然,解决方法是不重置count 成员:
def __next__(self):
if self.count < self.num:
v = self.count
self.count += 1
return v
else:
raise StopIteration
但现在列表推导必须在内循环中创建新对象:
>>> y = MyClass(5)
>>> print([[i for i in MyClass(5)] for i in y])
[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]
现在,假设我希望能够多次调用我的对象。我尝试使用以下方法实现非迭代器可迭代类:
class MyIterator():
def __init__(self, num):
self.num = num
self.count = 0
def __len__(self):
return self.num
def __iter__(self):
return self.my_iterator()
def my_iterator(self):
while self.count < self.num:
yield self.count
self.count += 1
self.count = 0
这很好用:
>>> x = MyIterator(5)
>>> print(list(x))
[0, 1, 2, 3, 4]
>>> print(list(x))
[0, 1, 2, 3, 4]
但是嵌套理解卡住了:
>>> x = MyIterator(5)
>>> print([[i for i in x] for i in x])
再次修复是删除重置内部计数器的行:
def my_iterator(self):
while self.count < self.num:
yield self.count
self.count += 1
并更改理解以在内循环中创建新对象:
>>> print([[i for i in MyIterator(5)] for i in x])
[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]
但“固定”类不能多次迭代:
>>> x = MyIterator(5)
>>> print(list(x))
[0, 1, 2, 3, 4]
>>> print(list(x))
[]
实现非迭代器可迭代的正确方法是什么(请注意,我*认为我遵循了this answer 中的最后一条评论)?还是 Python 明确不支持这个用例?
编辑:
rubber duck debugging的经典案例,我把最后一堂课改成:
class MyIteratorFixed():
def __init__(self, num):
self.num = num
def __len__(self):
return self.num
def __iter__(self):
return self.my_iterator_fixed()
def my_iterator_fixed(self):
count = 0
while count < self.num:
yield count
count += 1
我的错误在于我不需要 count 成员,因为 Python 已经拥有迭代器方法的状态(在本例中是 count 的值)。
>>> x = MyIteratorFixed(5)
>>> print(list(x))
[0, 1, 2, 3, 4]
>>> print(list(x))
[0, 1, 2, 3, 4]
>>> print([[i for i in x] for i in x])
[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]
我现在的问题是:
- 这是实现非迭代器可迭代的正确方法吗?
- 什么时候应该使用迭代器,什么时候应该使用非迭代器可迭代?只是其中一个只被调用一次的区别?
- 与迭代器相比,非迭代器可迭代器有哪些缺点?
谢谢!!
【问题讨论】:
-
问题在于
next不可重入:您试图使用单个属性self.count来跟踪多个独立迭代器的状态。您最后的尝试是正确的,因为my_iterator_fixed返回的generator对象通过返回自身正确实现了__iter__。 -
"与迭代器相比,非迭代器可迭代的缺点是什么?"问题是您将这些完全视为独立的事物,但实际上,重点在于 "非迭代器可迭代对象 返回一个保持其自身状态的迭代器 .这正是您遇到的问题。迭代器封装了实现迭代逻辑所需的状态。您的可迭代对象正在使用最终由所有迭代器共享的内部状态
标签: python python-3.x iterator iterable