当前位置: 首页 > 知识库问答 >
问题:

如何使一个类可迭代,但不修改迭代器,如果你修改了类?

蒋昊天
2023-03-14

我对python和编程都是新手,所以我很迷茫。我已经编写了一个“Node”类(如下文底部所示),它实例化了一个二叉搜索树和几个方法,如insert()和elements()(通过按顺序横切树返回元素列表)。我应该使这个类是可迭代的:“iter__(self)应该返回一个NodeIterator,它按照排序顺序从树中返回元素。修改树不应该修改现有的迭代器。”我现在正试图通过在类中插入以下代码来实现这一点:

 def __iter__(self):
    x=self.copy()
    x.i=0
    return x

def next(self):
    lst=x.elements()
    #??? No idea what to do from here.  

我定义了x=self.copy()以试图覆盖修改树不应该修改迭代器的事实,但我不知道这是不是正确的想法。下面是我的节点类,其中一个方法使用了装饰器:

def depth(old_f):
''' Write a decorator that overrides the standard behavior of
a function that returns True or False and instead returns
0 for False or n for number of recursive calls needed to
return True.
'''
    def new_f(*args):
        res = old_f(*args)
        if res is True:
            return 1
        elif res:
            return 1 + res
        else:
            return 0
    return new_f


Class Node(object):
'''
Modify your Node class from homework 4 as follows:

Two elements that compare equal shouldn't be allowed in the Tree. If a
copy is inserted, throw an AlreadyExistsException with the error
message "Element [x] is already in the tree".

__iter__(self) should return a NodeIterator, which returns elements from
the tree in sorted order. Modifying the tree should not modify
existing iterators.
'''
count = 0

def __init__(self, val, left=None, right=None):
    self.Val = val
    self.Left = left
    self.Right = right
    Node.count += 1

def __repr__(self):
    '''If the node has neither a left nor right child,
    simply return Node(val). Else, return Node(x, val, y),
    where x and y are recursive calls that return the
    left and right children, respectively.
    '''
    if self.Left is None and self.Right is None:
        return "Node({})".format(self.Val)
    else:
        return "Node({}, {}, {})".format(self.Left, self.Val, self.Right)

@depth
def search(self, element):
    ''' Finds whether a given element is in the tree.
    Returns True if the element is found, else returns False.

    Give it the depth decorator you defined earlier.
    '''
    if element == self.Val:
        return True
    elif self.Val > element and self.Left is not None:
        return self.Left.search(element)
    elif self.Val < element and self.Right is not None:
        return self.Right.search(element)
    else:
        return False

def insert(self, element):
    ''' Insert an element into a binary search tree rooted
    at this Node. After insertion, return the modified node.

    Our implementation will allow duplicate nodes. The left subtree
    should contain all elements <= to the current element, and the
    right subtree will contain all elements > the current element.
    '''
    if element <= self.Val:
        if self.Left is not None:
            self.Left.insert(element)
        else:
            self.Left = Node(element)
    else:
        if self.Right is not None:
            self.Right.insert(element)
        else:
            self.Right = Node(element)
    return self

def elements(self):
    ''' Return a list of the elements visited in an inorder traversal:
    http://en.wikipedia.org/wiki/Tree_traversal
    Note that this should be the sorted order if you've inserted all
    elements using your previously defined insert function.
    '''
    if self.Left is None and self.Right is None:
        return [self.Val]
    elif self.Left is None and self.Right is not None:
        return [self.Val] + self.Right.elements()
    elif self.Left is not None and self.Right is None:
        return self.Left.elements() + [self.Val]
    else:
        return self.Left.elements() + [self.Val] + self.Right.elements()

共有1个答案

荆弘伟
2023-03-14

不要在类上定义next;这使得实例成为迭代器(它必须直接迭代对象),而您想要的是一个不是迭代器的iterable。为此,定义__iter__并让它在对象的副本上返回一个全新的迭代器。

由于您已经有了一个elements方法,可以有效地快照您的节点,因此使用它来创建快照迭代器并不难;复制结构是没有意义的,因为您只是按顺序迭代,而elements可以为您完成这项工作(并且在启动快照时使用更少的内存)。通常,我只需要使__iter__成为生成器函数,例如:

 def __iter__(self):
     # Python 3 simple version
     yield from self.elements()
     # Python 2 or 3 version
     for x in self.elements():
         yield x

但是由于您的分配调用了一个特殊的NodeIterator类,所以这是不行的。为此,您需要创建一个新类(可能在现有的node类中定义,以便为其命名):

class Node(object):
    ...
    class NodeIterator(object):
        def __init__(self, node):
            self.it = iter(node.elements())

        def __iter__(self):
            return self

        def next(self):
            return next(self.it)
        __next__ = next  # Makes code work on Py2 and Py3 without modification

    def __iter__(self):
        return NodeIterator(self)

您可以理解为什么我通常不需要使用特殊类,因为__iter__的生成器函数(使用yield魔术的函数)要简单得多,但这是基本结构。您可以在Python wiki上了解更多关于迭代器的信息,或者在PythoncollectionsABC文档中了解基本接口的信息。

 类似资料:
  • 问题内容: 假设我有一个整数集,并且我想增加集合中的每个整数。我该怎么做? 我可以在迭代时添加和删除集合中的元素吗? 在迭代原始集合时,是否需要创建一个新集合以将元素“复制并修改”到其中? 编辑:如果集合中的元素是不可变的呢? 问题答案: 您可以在迭代期间使用Iterator对象安全地从集合中删除;尝试在迭代时通过其API修改集合会破坏迭代器。Set类通过getIterator()提供一个迭代器。

  • 我有一个json blob,如下所示: 这只是一个在这里发布的例子。在这个例子中,我们看到它提供了一个学校每年发生的所有事件的列表,该列表是一个累积列表,即上一年的事件也会被追加。我们知道的是,每年的活动都将以“会议开始”活动开始。我想通过这件事,以最新的一系列事件结束。请忽略事件名称中的时间戳或年份,它们只是示例,我在现实世界中没有此类信息。我需要的最终结果是: 所以,我想保留从“会话开始”事件

  • 问题内容: 我知道您不应在遍历列表时添加/删除项目。但是,如果不更改列表长度,是否可以修改要迭代的列表中的项目? 还是应该迭代列表索引?像那样: 问题是:以上两种方式都是允许的,还是只有第二种是没有错误的? 如果答案是肯定的,以下代码段是否有效? UPD。我想在python文档中看到“允许这些操作”而不是某人的假设。 问题答案: 可以这么说,您 不是在 修改列表。您只是在修改列表中的元素。我不认为

  • 问题内容: 上面的python代码使输出与预期的完全不同。我想循环播放项目,以便在循环播放时可以跳过某个项目。 问题答案: 该代码试图在迭代列表时修改列表。我在任何情况下都不会这样做。 你可以使用运算符跳过列表中的每三个项目。 关于我的示例的其他要点与中的新语法有关。 我使用列表推导定义t,因为它在中有效(请参见下文) t是中的函数 现在,的行为类似于,但它适用于任意大小的值。后者不再存在。

  • 问题内容: 假设我们有一个Python字典,我们正在像这样迭代它: (并且仅仅是一些黑盒转换。) 换句话说,我们尝试在使用对其进行迭代的同时向其中添加/删除项目。 这个定义好吗?您能否提供一些参考来支持您的答案? (很明显,如果损坏了该如何解决,所以这不是我所追求的角度。) 问题答案: 在python文档页面(针对2.7)上明确提到了 使用而添加或删除字典条目可能会产生一种或无法遍历所有条目。 对

  • 我对java相当陌生,实际上我正在编写一个键盘记录器,并让它定期写入文件。每当用户按下某个键时,它都会实例化一个NativeKeyEvent,该事件调用“param string()”并将信息作为字符串添加到下面的arraylist中... 然后,在每一个间隔,字符串数组被传递,并被写入下面的TimerTask线程中的文件。 行'str=iterator.next().tostring();‘然后