Python 为什么翻转元组赋值可以改变其行为?
一直以来,我都有这样的印象,Python 为什么翻转元组赋值可以改变其行为?,python,variable-assignment,Python,Variable Assignment,一直以来,我都有这样的印象,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):
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=节点(v=1,nextV=2)
但是,如果我像这样重新排列翻转函数:
def flip(nodeA, nodeB):
nodeB, nodeA, nodeA.next = nodeA, nodeA.next, nodeB
return (nodeA, nodeB)
…输出被破坏:
A=Node(v=2,nextV=2);B=节点(v=1,nextV=2)
节点A以一个指向自身的指针结束(其nextV
和v
是相同的),因此跟随此树的尝试将永远重复
为什么这些结果不相同?元组解包的行为不应该像所有赋值同时发生一样吗?因为您要修改的一个项是另一个项的属性,这些项不是相互独立的——需要序列化顺序来确定操作将执行的操作,并且该操作是从左到右的 让我们通过编写与临时变量相同的代码来了解这一点
鉴于以下共同的前奏:
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
指两种情况下不同nodeA
的next
属性。
让我们看看在运行时,当一切正常时,这是如何实现的,一些伪代码显示了对象ID,这样您就可以区分原地变异的对象和更改引用的对象:
#工作实现
###############################################################
#id(nodeA)#id(nodeB)#AAA.v#AAA.next#BBB.v#BBB.next#
###############################################################
#AAA#BBB#1#BBB#2#无#启动条件
#AAA#AAA#1#BBB#2#无#节点B=旧节点A
#AAA#AAA#1#BBB#2#无#nodeA.next=旧的#nodeB
#BBB#AAA#1#BBB#2#无#nodeA=旧#nodeA#下一个
在工作场景中,我们将A
和B
的名称切换到每个指向相反节点的名称;其他一切都没有改变
相比之下:
#执行失败
###############################################################
#id(nodeA)#id(nodeB)#AAA.v#AAA.next#BBB.v#BBB.next#
###############################################################
#AAA#BBB#1#BBB#2#无#启动条件
#AAA#AAA#1#BBB#2#无#节点B=旧节点A
#BBB#AAA#1#BBB#2#无#nodeA=旧#nodeA#下一个
#BBB#AAA#1#BBB#2#BBB#nodeA.next=旧nodeB
当我们到达
nodeA.next=old_nodeB
时,名称nodeA
已经被分配了最初与节点B关联的id(BBB
),因此我们将原始节点B的next
指针改为指向自身,在问题的核心生成循环。@CharlesDuffy,在那里,高兴吗?我回答了python是如何从左到右(不是同时)赋值的,但是看到代码我不确定问题在于赋值,而不是赋值的含义,它是一个无限递归的外壳loop@Raksha“请尊重我。”如我所说,拉克沙,请尊重它。给人打电话是不会帮你得到答案的。这是必要的。在你提供答案后,我将尝试回答你的问题。我也将撤回我的否决票和结束票。顺便说一句,如果我试图编辑问题以获得适当的MCVE,你会生气吗?我不明白这意味着什么。。。。什么“不同”c?这个“不同”的c没有下一个吗?它到底在哪一点上被打破了?我添加的评论有帮助吗?不是真的。。。这是=
创建指针而不是像列表那样创建副本的情况之一吗?因此,old\u c\u next
不仅仅是一个节点本身,即使它是一个新变量。。。我只是想把整个过程形象化,记住nodeA.next
是一个不同的变量,取决于nodeA
是什么。因此,如果您在更改nodeA.next
之前更改nodeA
,您正在修改哪个对象具有作为分配目标的next
属性。@Raksha,顺便说一句,我的表中有一个错误,来自最初组装的损坏版本;见更正。