我试图弄清楚如何制作迭代器,下面是一个工作正常的迭代器。
class DoubleIt:
def __init__(self):
self.start = 1
def __iter__(self):
self.max = 10
return self
def __next__(self):
if self.start < self.max:
self.start *= 2
return self.start
else:
raise StopIteration
obj = DoubleIt()
i = iter(obj)
print(next(i))
但是,当我尝试将16传递给iter()中的第二个参数时(我希望迭代器在返回16时停止)
i = iter(DoubleIt(), 16)
print(next(i))
抛出TypeError:iter(v,w):v必须是可调用的 因此,我试着这样做。
i = iter(DoubleIt, 16)
print(next(i))
它返回&lt; main .DoubleIt对象,位于0x7f4dcd4459e8&gt;。这不是我的预期。 我检查了programiz的网站,https://www.programiz.com/python-programming/methods/built-in/iter 其中说可调用对象必须在第一个参数中传递以便使用第二个参数,但它没有提及可以在其中传递用户定义的对象以使用第二个参数。
所以我的问题是,有没有办法这样做?第二个参数可以与&#34;自定义对象&#34;?
一起使用答案 0 :(得分:9)
documentation可能会更明确一点,它只会说明
iter( object [, sentinel ])
...
在这种情况下创建的迭代器将调用 object 而不带参数 每次调用
__next__()
方法;如果返回的值等于sentinel,则会引发StopIteration
,否则将返回该值。
可能没有说清楚的是,迭代器产生的是无法调用的返回值。因为你的callable是一个类(没有参数),所以每次迭代都会返回一个类的新实例。
解决此问题的一种方法是让您的类可调用并将其委托给__next__
方法:
class DoubleIt:
def __init__(self):
self.start = 1
def __iter__(self):
return self
def __next__(self):
self.start *= 2
return self.start
__call__ = __next__
i = iter(DoubleIt(), 16)
print(next(i))
# 2
print(list(i))
# [4, 8]
这具有以下优点:它是一个无限生成器,仅由iter
的标记值停止。
另一种方法是使该类的最大参数:
class DoubleIt:
def __init__(self, max=10):
self.start = 1
self.max = max
def __iter__(self):
return self
def __next__(self):
if self.start < self.max:
self.start *= 2
return self.start
else:
raise StopIteration
i = iter(DoubleIt(max=16))
print(next(i))
# 2
print(list(i))
# [4, 8, 16]
需要注意的一个区别是iter
在遇到哨兵值时会停止(并且不会产生项目),而第二种方式则使用<
,而不是<=
比较(喜欢你的代码),因此会产生最大的项目。
答案 1 :(得分:1)
这是一个双重例程的例子,可用于iter
的两种参数模式:
count = 1
def nextcount():
global count
count *= 2
return count
print(list(iter(nextcount, 16)))
# Produces [2, 4, 8]
此模式涉及iter
为我们创建iterator。请注意,我们需要重置count
才能再次使用它;它仅适用于具有副作用的可调用(例如函数或绑定方法)(更改计数器),并且迭代器仅在遇到完全 sentinel值时停止。
您的DoubleIt
课程没有提供设置max
值的特定协议,iter
也没有预期或使用任何此类协议。 iter
的备用模式从可调用值和标记值创建迭代器,完全独立于iterable或iterator协议。
您期望的行为更类似于itertools.takewhile或itertools.islice所做的行为,操纵一个迭代器来创建另一个迭代器。
制作可迭代对象的另一种方法是实现sequence协议:
class DoubleSeq:
def __init__(self, steps):
self.steps = steps
def __len__(self):
return self.steps
def __getitem__(self, iteration):
if iteration >= self.steps:
raise IndexError()
return 2**iteration
print(list(iter(DoubleSeq(4))))
# Produces [1, 2, 4, 8]
请注意DoubleSeq
根本不是迭代器; iter
使用序列协议为我们创建了一个。 DoubleSeq
没有迭代计数器,迭代器也没有。