为什么生成器只使用for循环前进一次



我很惊讶在互联网上找不到类似的东西。

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,所以看起来您立即完成了操作。

解决方案:

  1. yielding之前缓存next
  2. 在移除之前,让removeAllNodes将所有节点缓存到list
  3. 只需跳过所有这些废话,将self.head = self.tail = None设置为removeAllNodes中的唯一代码,并让垃圾收集器处理清理现在未引用的节点(如果涉及引用循环,或者您没有使用CPython引用解释器,这可能不是立即的,但在清理之前通常可以有一个短暂的延迟;如果需要,您可以使用某种类型的weakref代理进行反向链接,以避免循环(

相关内容

  • 没有找到相关文章

最新更新