【问题标题】:confused about the exact workings of next() and __iter__()对 next() 和 __iter__() 的确切工作感到困惑
【发布时间】:2015-07-28 18:28:10
【问题描述】:

在尝试实现迭代器时,我对 next() 感到困惑。我制作了一个简单的测试脚本,其中迭代器按预期工作:

class Object:
    def __init__(self, name):
        self.name = name
    def prin(self):
        print self.name

class Some:
    def __init__(self):
        self.data = list()
    def __iter__(self):
        return self.SomeIter(self, len(self.data))
    def fill(self, obj):
        self.data.append(obj)
    def printMe(self):
        for entry in self:
            print entry.name

    class SomeIter:
        def __init__(self, some, end):
            self.index = 0
            self.end = end
            self.name = some.data[self.index].name
            self.data = some.data
        def next(self):
            if self.index == self.end:
                raise StopIteration
            else:
                self.name = self.data[self.index].name
                self.index += 1
            return self

########################################################################


someX = Some()

obj1 = Object("A")
obj2 = Object("B")

someX.fill(obj1)
someX.fill(obj2)
someX.fill(obj2)

for obj in someX:
    print obj.name

我得到“A B B”作为输出。这都很好。但是我也有一个树类的迭代器。 next() 方法的工作原理基本相同。我首先更新实例然后返回它。但是在树迭代器的情况下,第一个元素被跳过。这对我来说很有意义,因为我只有在更新实例后才返回 self 。但是为什么在上述实现的情况下我会得到不同的行为,实例正在更新,然后才返回呢?

########################################################################
# RIGHT-HAND-CORNER-BOTTOM-UP-POST-ORDER-TRAVERSAL-ITERATOR
########################################################################
    class RBPIter:
        """!
        @brief Right hand corner initialised iterator, traverses tree bottom
                     up, right to left
        """
        def __init__(self, tree):
            self.current = tree.get_leaves(tree.root)[-1] # last leaf is right corner
            self.csi = len(self.current.sucs)-1 # right most index of sucs
            self.visited = list() # visisted nodes
            self.tree = tree
            self.label = self.current.label
########################################################################
        def __iter__(self):
            return self
########################################################################
        def begin(self):
            return self.tree.get_leaves(self.tree.root)[-1]
########################################################################
        def end(self):
            return self.tree.root
########################################################################
        def find_unvisited(self, node):
            """!
            @brief finds rightmost unvisited node transitively dominated by node
            """
            leaves = self.tree.get_leaves(self.tree.root)
            # loop through leaves from right to left, as leaves are listed
            # in order, thus rightmost list elememt is rightmost leaf
            for i in range(len(leaves)-1, -1, -1):
                # return the first leaf, that has not been visited yet
                if leaves[i] not in self.visited:
                    self.label = leaves[i].label
                    return leaves[i]
            # return None if all leaves have been visited
            return None
########################################################################
        def go_up(self, node):
            """!
            @brief sets self.current to pred of self.current,
                         appends current node to visited nodes, reassignes csi
            """
            self.visited.append(self.current)
            self.current = self.current.pred
            if self.current.sucs[0] not in self.visited:
                self.current = self.find_unvisited(self.current)
            self.label = self.current.label
            self.csi = len(self.current.sucs)-1
            self.visited.append(self.current)
########################################################################
        def next(self):
            """!
            @brief advances iterator
            """
            # if current node is a leaf, go to its predecessor
            if self.current.suc_count == 0 or self.current in self.visited:
                self.go_up(self.current)
            # if current node is not a leaf, find the next unvisited
            else:
                self.current = self.find_unvisited(self.current)
            if self.current == self.end():
                raise StopIteration
            return self

编辑 1:

我比较了两个迭代器的输出,它们不同,SomeIter 输出第一个元素 2 次:

next:  A
A
next:  A
B
next:  B
B
next:  B

另一个迭代器没有:

next:  a
s
next:  s
i
next:  i
r
next:  r
t
next:  t
t
next:  t
s
next:  s
e
next:  e
t
next:  t

否则 "next: a" 会出现 2 次​​p>

编辑 2:

这对我来说真的没有任何意义......

查看这些调用和输出:

someXIter = iter(someX)
print someXIter.next().name
print someXIter.next().name
print someXIter.next().name

输出:

next:  A
A
next:  A
B
next:  B
B

使用此代码:

class SomeIter:
        def __init__(self, some, end):
            self.index = 0
            self.end = end
            self.name = some.data[self.index].name
            self.data = some.data
        def next(self):
            print "next: ", self.name
            if self.index == self.end:
                raise StopIteration
            else:
                self.name = self.data[self.index].name
                self.index += 1
            return self

为什么这对我来说毫无意义?因为,第一次调用 next() 时,它会打印“next: A”,然后更新实例,并打印函数调用的返回值,也就是“A”。哇?为什么不是“B”,返回值应该是更新后的实例。

【问题讨论】:

  • 您能否尽量减少代码量来重现您的问题? stackoverflow.com/help/mcve
  • 但是第一段代码已经是一个最小化的例子了... ?它也包含执行代码。输出也在我的帖子中。我不明白你想让我怎么把它最小化。
  • 为什么将SomeIter 实现为一个类?
  • 一是因为我认为迭代器必须是对象,二是因为我在实际程序中需要多个迭代器。
  • 您是否对next 或您的实现感到困惑? next 应该返回下一项并在用尽时提高 StopIteration。哪个版本的 Python?为什么不使用 new-style 类?

标签: python iterator next


【解决方案1】:

Python 2.7

要成为迭代器必须实现迭代器协议:

  • 定义obj.__iter__obj.next
  • obj.__iter__ 必须返回 self
  • 一旦引发StopIteration,随后对obj.next() (next(obj)) 的调用必须引发StopIteration

如果一个类只定义了 __iter____iter__ 必须返回一个实现迭代器协议的对象。如果类中的项目包含在内置类型中,如列表,__iter___ 可以简单地返回iter(list)

我想整个概念中隐含的是迭代器必须跟踪它在迭代中的位置。

如果您希望您的对象成为具有不同迭代序列的迭代器,您可以定义不同的方法来跟踪迭代并前进到下一项,然后在obj.next() 中使用这些方法。

简单的例子:

class Thing(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return self.name
    def __repr__(self):
        return 'Thing({})'.format(self.name)

class Some(object):
    def __init__(self):
        self.data = None
        # need something to keep track of the iteration sequence
        self.__index = None
        # type of iteration do you want to do
        self.direction = self.forward
    def __iter__(self):
        # reset the iteration
        self.__index = None
        return self
    def next(self):
        try:
            return self.direction()
        except IndexError:
            raise StopIteration
    def forward(self):
        if self.__index is None:
            self.__index = -1
        self.__index += 1
        return self.data[self.__index]
    def reverse(self):
        if self.__index is None:
            self.__index = 0
        self.__index -= 1
        return self.data[self.__index]

用法

>>> some = Some()
>>> some.data = [Thing('A'),Thing('B'),Thing('C'),Thing('D')]
>>> for thing in some:
    print thing,

A B C D
>>> some.direction = some.reverse
>>> for thing in some:
    print thing,

D C B A
>>> 

所以也许只是保持next 简单,并将树遍历逻辑放在不同的方法中。它可能使测试该逻辑更容易。而且你总是可以添加不同的行为:

    def odd_then_even(self):
        if self.__index is None:
            self.__index = -1
            self.__odd = True
        self.__index += 2
        try:
            return self.data[self.__index]
        except IndexError:
            if not self.__odd:
                raise IndexError
            self.__odd = False
            self.__index = 0
            return self.data[self.__index]

>>> some.direction = some.odd_then_even
>>> for thing in some:
    print thing,


B D A C
>>>

我很难理解您的 inner class 解决方案是如何工作的,但看起来不对劲的一件事是,您的迭代器对象的 next 方法正在返回 self 并且看起来像 @ 987654339@ 应该返回序列/集合中的下一项。当您迭代一组事物时,迭代应该提供单个事物,而不是迭代器对象的修改实例。

【讨论】:

    猜你喜欢
    • 2015-01-13
    • 2023-04-08
    • 2011-10-13
    • 1970-01-01
    • 2021-09-16
    • 2015-11-12
    • 2012-08-24
    • 2019-03-18
    • 2021-06-24
    相关资源
    最近更新 更多