iter()方法的第二个参数

时间:2018-06-14 09:13:56

标签: python python-3.x

我试图弄清楚如何制作迭代器,下面是一个工作正常的迭代器。

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;?

一起使用

2 个答案:

答案 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 的备用模式从可调用值和标记值创建迭代器,完全独立于iterableiterator协议。

您期望的行为更类似于itertools.takewhileitertools.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没有迭代计数器,迭代器也没有。