为什么翻转元组分配会改变其行为?

时间:2018-09-23 00:44:10

标签: python variable-assignment

一直以来,我一直以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相同),因此尝试遵循此树将永远递归。

为什么这些结果不一样?元组拆箱是否应该表现为所有作业同时发生?

1 个答案:

答案 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指针,在问题的核心产生了循环。