python 类对象的可变性何时影响赋值?

When does mutability of python class objects affect assignments?

提问人:user6175310 提问时间:7/21/2016 最后编辑:John Kugelmanuser6175310 更新时间:7/21/2016 访问量:192

问:

我对 python 对类/对象的可变特性的理解是,如果您进行赋值,那么对原始变量的任何更改也会更改分配的变量/对象。我对下面的这段代码感到困惑。

# Recursive solution to Flatten Binary Tree to Linked List by LeetCode

# Definition for a  binary tree node
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    # @param root, a tree node
    # @return root, a tree node
    def flattenHelper(self, root):
        if root == None:
            return None
        else:
            left = root.left
            right = root.right
            root.left = None    # Truncate the left subtree
            current = root

            # Flatten the left subtree
            current.right = self.flattenHelper(left)
            while current.right != None:    current = current.right

            # Flatten the right subtree
            current.right = self.flattenHelper(right)
            return root

    # @param root, a tree node
    # @return nothing, do it in place
    def flatten(self, root):
        self.flattenHelper(root)
        return

问题:为什么变量不会自动设置为执行一次?leftNoneroot.left = None

对象 可变

评论

1赞 joel goldstick 7/21/2016
root.left = None 将名称 root.left 设置为绑定到 None。它不会改变左边的必然。所以节点仍然存在,因为有东西在引用它。只是root.left不再引用它了
0赞 user6175310 7/21/2016
@joelgoldstick 谢谢,但我仍然很困惑:如果我执行以下操作:test=root, root.left=TreeNode(5), test.left.val 现在将是 5 为什么上面没有发生同样的事情?
0赞 user6175310 7/21/2016
@joelgoldstick 我认为这里正在发生类似于列表可变性的事情:如果我有 a=[1,2,3],那么定义 b=a,现在如果我更改 a[0]=0,b[0] 也会自动更改。我用对象尝试了这个,就像上面的例子一样,发生了类似的事情。我不明白这段代码的实现有什么不同,左边不会自动设置为无。
0赞 user2357112 7/21/2016
“我对 python 对类/对象的可变特性的理解是,如果你进行赋值,那么对原始内容的任何更改也会改变副本。”没有副本。请参阅 nedbatchelder.com/text/names.html
0赞 pretzlstyle 7/21/2016
@user6175310你的例子涉及 ,你正在复制对象本身,所以变量当然会与来自同一对象的变量相同,因为它们实际上是同一个变量。test=rootleftleft

答:

2赞 kindall 7/21/2016 #1

Python 中的赋值始终以相同的方式工作。它将符号左侧的事物更改为引用右侧表达式的值。正如您在评论中询问的那样,绝对没有任何“实现不同”。=

有时,左侧的项目是容器中的插槽(列表、字典、对象)。这些对象是可变的(可以更改),因此您可以更改其插槽所引用的内容。当您这样做时,例如:

a = b = [0]

现在和是同一对象的两个不同名称。如果这样做,则也变为 1,因为 和 是同一个对象,并且赋值不会改变这一点,因为您正在赋值给 引用的对象中的插槽 0;你不是在改变它本身所指的东西。但是,如果您改为 ,则保持 0,因为现在指向与 不同的列表。aba[0] = 1b[0]abaaa = [1]b[0]ab

这就是您的示例中发生的情况。名称和最初引用同一对象。当您更改为指向其他对象时,它不会更改为指向同一对象。要做到这一点,必须是一个容器,并且它必须与 、 和 是同一个容器,并且将要改变的是 ,而不是它本身。因为除了赋值之外,您无法通过任何其他方式更改名称的值。leftroot.leftroot.leftleftleftrootroot.leftleft.leftleft

评论

0赞 user6175310 7/21/2016
谢谢!所以基本上,如果不是将root.left分配给None,而是对对象进行了一些更改(例如,这将更改为left--->也会变为)这是正确的吗?root.left.left = Noneleft.leftNone
0赞 kindall 7/21/2016
类似的东西。经验法则是,只有当赋值到对象中的某个插槽(例如列表或字典项,或对象属性)时,您才会看到对一个对象的更改反映在另一个对象中,并且只有当另一个引用指向同一包含对象(或父对象, 完成)。
0赞 user6175310 7/25/2016
另一个问题,此时 while 循环中的每次都被分配给 so 并且不再指向同一个树对象,对吧?如果是这种情况,则返回 In End。如何不断更新?无论发生什么,为什么它不等于 ,即为什么不返回?currentcurrent.rightrootcurrentrootrootcurrentcurrent
0赞 kindall 7/25/2016
该函数是递归的。处理根节点后,它会调用自己来处理其左右子节点。这些函数调用对其子函数执行相同的操作,依此类推。 永远不会在初始函数中更新,而是递归调用中的不同节点。rootroot