我很惊讶在互联网上找不到类似的东西。
class Node:
def __init__(self, value):
self.value = value
self.prev = None
self.next = None
class DoublyLinkedList:
def __init__(self):
self.head = None
self.tail = None
# bunch of other methods here removed for clarity
def removeNodesWithValue(self, value):
for node in self._getAllNodes(value):
print('removing')
self._remove(node)
# I haven't provided this method before I got the answer
def _remove(self, node):
...
node.prev = None
node.next = None
...
def _getAllNodes(self, value):
cur = self.head
while cur is not None:
if cur.value == value:
yield cur
cur = cur.next
if __name__ == '__main__':
l = DoublyLinkedList()
l.insertAfter(l.head, Node(1))
l.insertAfter(l.head, Node(1))
l.insertAfter(l.head, Node(1))
l.removeNodesWithValue(1)
此代码打印如下:
removing
但我希望它为每个找到的节点打印3次。现在,如果我把这行for node in self._getAllNodes(value)
改成for node in list(self._getAllNodes(value))
,那么它就会按预期打印3次。
有人知道为什么for循环只从生成器中获取一个元素而不是全部3个元素吗?
完整代码:https://pastebin.com/QEasZSnK
更新:
根据ShadowRanger的回答,将_getAllNodes
更改为这个可以修复问题:
def _getAllNodes(self, value):
cur = self.head
while cur is not None:
next = cur.next
if cur.value == value:
yield cur
cur = next
您忽略了提供remove
的定义,但心理调试说它None
去掉了要删除的节点的next
属性。问题是,生成器在yield
处暂停,将节点交还给调用方,然后由remove
调用。当生成器恢复时,它仍然具有对已删除节点的引用,并尝试从中获取next
属性,但next
现在是None
,所以看起来您立即完成了操作。
解决方案:
- 在
yield
ing之前缓存next
- 在移除之前,让
removeAllNodes
将所有节点缓存到list
- 只需跳过所有这些废话,将
self.head = self.tail = None
设置为removeAllNodes
中的唯一代码,并让垃圾收集器处理清理现在未引用的节点(如果涉及引用循环,或者您没有使用CPython引用解释器,这可能不是立即的,但在清理之前通常可以有一个短暂的延迟;如果需要,您可以使用某种类型的weakref
代理进行反向链接,以避免循环(