双链接标记列表中的__next__和__iter__方法

时间:2017-06-19 14:44:35

标签: python python-3.x doubly-linked-list

我现在正在处理一个双向链接列表类,我的下一个和它的方法遇到了麻烦。这是我已经上交的课程项目,现在只是想了解如何实际修复它以使其有用。

我想让我的代码做的是设置一个当前指针,从标题开始,然后继续前进直到指示终止或它到达预告片。我想访问存储在每个节点的值。节点类是主链表类的子类。这是我的代码。当我调用我的方法(发布我的追加方法)时出现我的问题;无法识别当前指针。关于如何解决这个问题的任何想法?

class Linked_List:

    class __Node:
        def __init__(self, val):
          self.val = val
          self.size = 0

    def __init__(self):
        self.header = Linked_List.__Node('header')
        self.trailer = Linked_List.__Node('trailer')
        self.header.next = self.trailer
        self.trailer.prev = self.header
        self.size = 0
        self.current = self.header
        self.current.next = self.trailer

    def __iter__(self):
        self.current = self.header
        return self


    def __next__(self):
        if self.current == self.trailer:
            raise StopIteration
        result = self.Linked_List.__Node[self.current]
        self.current = self.current.next
        return result

    def append(self, val):
        new_node = Linked_List.__Node(val)

        if self.header.next is self.trailer:
            self.header.next = new_node
            self.trailer.prev = new_node
            self.current = self.header
        else:
            while self.current is not self.trailer:
                self.current = self.current.next
            self.current.next = new_node
            new_node.next = self.trailer
            new_node.prev = self.current
        self.size += 1

我是python的新手(以及一般的编码)所以任何建议都会很棒。

1 个答案:

答案 0 :(得分:2)

您的代码有多个问题,在您尝试使用时会显现出来。让我们假设以下代码来测试它:

l = Linked_List()
l.append('foo')
l.append('bar')
l.append('baz')

print([x.val for x in l])
  

AttributeError:' __ Node'对象没有属性' next'

第一期:您的__Node类型没有nextprev的字段:

class __Node:
    def __init__(self, val):
      self.val = val
      self.size = 0
      self.prev = None
      self.next = None
  

AttributeError:' NoneType'对象没有属性' next'

第二个问题:并非总是为附加节点填充next。在append中的一个路径中,您未设置新节点的nextprev

def append(self, val):
    new_node = Linked_List.__Node(val)

    if self.header.next is self.trailer:
        # set the attributes on new_node
        new_node.prev = self.header
        new_node.next = self.trailer

        self.header.next = new_node
        self.trailer.prev = new_node
        self.current = self.header
    # …
  

AttributeError:' Linked_List'对象没有属性' Linked_List'

第三个问题:不知道你在__next__那里想做什么。您只需访问self.current那里:

def __next__(self):
    if self.current == self.trailer:
        raise StopIteration
    result = self.current
    self.current = self.current.next
    return result

一旦我们修复了所有这些,我们就有了一个成功运行的代码。但我们只得到以下输出:['header', 'foo']。当然,这不是我们想要的。

发生这种情况的原因是因为物品的实际顺序如下:

header
foo
trailer
baz
trailer

(是的,有一个递归)显然,append毕竟不能正常工作。如果您只是附加两个元素,则可以看到尾部元素之后添加了元素。这意味着self.current确实会在追加循环中点击尾部元素:

while self.current is not self.trailer:
    self.current = self.current.next

如果你仔细观察,就会发生这种情况:首先更新self.current,然后进行检查以最终取消循环。那时self.currentself.trailer。所以我们应该检查self.current.next

while self.current.next is not self.trailer:
    self.current = self.current.next

修复后,我们得到以下输出:['header', 'foo', 'bar', 'baz']。这几乎是我们想要看到的。我们现在需要做的就是跳过header元素。我们只需从标题后的元素开始:

def __iter__(self):
    self.current = self.header.next
    return self

然后它有效。

这就是让代码运行所需的一切。 但是,我通常会建议不要采用这种做法。您正在列表中存储迭代状态,这非常脆弱。您应该尽可能将此状态设置为本地状态。

特别是,链接列表不需要是可枚举枚举器。前者实现__iter__,后者实施__next__。可枚举的意思是“你可以迭代这个东西”,而枚举器是执行迭代并具有迭代状态的东西。

尝试移动迭代状态 off 链接列表,使其只能枚举而不是枚举器。为此,请添加LinkedListEnumerator类型,该类型具有对列表的引用并跟踪当前元素:

class LinkedListEnumerator:
    def __init__ (self, lst):
        self.lst = lst
        self.current = lst.header.next

    def __iter__ (self):
        return self

    def __next__ (self):
        if self.current == self.lst.trailer:
            raise StopIteration
        result = self.current
        self.current = self.current.next
        return result

然后,您可以删除链接列表中的__next__方法,并使用以下内容替换__iter__方法:

def __iter__(self):
    return LinkedListEnumerator(self)

然后你不再拥有链表中的状态。 (此时,您还应该在currentappend一个本地变量并完全摆脱self.current