一直以来,我一直以window.print()
与a, b, c = c, a, b
相同的印象...我认为这是同时分配变量的一种方式,因此您不必必须创建一堆临时变量。但是显然它们是不同的,因为它破坏了我的代码。
这是我原来的/有效的实现方式:
a, c, b = c, b, a
它的正确/预期行为是交换链接列表中的两个节点,如下面的输出所示:
class Node:
def __init__(self, v = None, next = None):
self.v = v
self.next = next
def __repr__(self):
return "Node(v=%r, nextV=%r)" % (self.v, self.next.v if self.next else None)
a = Node(1)
b = Node(2)
a.next = b
def flip(nodeA, nodeB):
nodeB, nodeA.next, nodeA = nodeA, nodeB, nodeA.next
return (nodeA, nodeB)
a, b = flip(a, b)
print "A=%r; B=%r" % (a, b)
但是,如果我像这样对翻页功能重新排序:
A=Node(v=2, nextV=None); B=Node(v=1, nextV=2)
...该输出已损坏:
def flip(nodeA, nodeB):
nodeB, nodeA, nodeA.next = nodeA, nodeA.next, nodeB
return (nodeA, nodeB)
节点A最终指向其自身的指针(其A=Node(v=2, nextV=2); B=Node(v=1, nextV=2)
和nextV
相同),因此尝试遵循此树将永远递归。
为什么这些结果不一样?元组拆箱是否应该表现为所有作业同时发生?
答案 0 :(得分:2)
由于您要修改的一项是另一项的属性,因此它们并不相互独立-需要序列化顺序来确定该操作将要执行的操作,并且该操作是从左到右的
让我们看一下如何发挥作用,方法是像编写临时变量一样编写这段代码。
给出以下共同的序言:
old_nodeA = nodeA
old_nodeB = nodeB
old_nodeA_next = nodeA.next
工作代码类似于以下内容:
# nodeB, nodeA.next, nodeA = nodeA, nodeB, nodeA.next
nodeB = old_nodeA
nodeA.next = old_nodeB # nodeA is still the same as old_nodeA here
nodeA = old_nodeA_next
这是坏的:
# nodeB, nodeA, nodeA.next = nodeA, nodeA.next, nodeB
nodeB = old_nodeA
nodeA = old_nodeA_next
nodeA.next = old_nodeB # we're changing old_nodeA_next.next, not old_nodeA.next
区别在于nodeA.next
指的是两种情况下不同next
的{{1}}属性。
让我们看一下在一切正常的情况下在运行时如何工作,其中一些伪代码显示了对象ID,因此您可以区分就地更改对象和更改引用的对象:
nodeA
在工作方案中,我们将# Working implementation
###############################################################
# id(nodeA) # id(nodeB) # AAA.v # AAA.next # BBB.v # BBB.next #
###############################################################
# AAA # BBB # 1 # BBB # 2 # None # Starting condition
# AAA # AAA # 1 # BBB # 2 # None # nodeB = old_nodeA
# AAA # AAA # 1 # BBB # 2 # None # nodeA.next = old_nodeB
# BBB # AAA # 1 # BBB # 2 # None # nodeA = old_nodeA_next
和A
的名称切换为分别指向相反的节点;没有其他改变。
对比:
B
到达# Broken implementation
###############################################################
# id(nodeA) # id(nodeB) # AAA.v # AAA.next # BBB.v # BBB.next #
###############################################################
# AAA # BBB # 1 # BBB # 2 # None # Starting condition
# AAA # AAA # 1 # BBB # 2 # None # nodeB = old_nodeA
# BBB # AAA # 1 # BBB # 2 # None # nodeA = old_nodeA_next
# BBB # AAA # 1 # BBB # 2 # BBB # nodeA.next = old_nodeB
时,已经为名称nodeA.next = old_nodeB
分配了最初与节点B相关的ID(在我们的示例中为nodeA
),因此我们更改了原始节点B的指向其自身的BBB
指针,在问题的核心产生了循环。