了解Python中对类对象的可变性和多变量分配 [英] Understanding Mutability and Multiple Variable Assignment to Class Objects in Python

查看:71
本文介绍了了解Python中对类对象的可变性和多变量分配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找有关可变性和类对象的一些说明.据我了解,Python中的变量是关于为对象分配变量名的.

I'm looking for some clarification regarding mutability and class objects. From what I understand, variables in Python are about assigning a variable name to an object.

如果该对象是不可变的,则当我们将两个变量设置为同一对象时,它将是两个单独的副本(例如a = b = 3,所以更改为4不会影响b,因为3是一个数字,例如不变的对象).

If that object is immutable then when we set two variables to the same object, it'll be two separate copies (e.g. a = b = 3 so a changing to 4 will not affect b because 3 is a number, an example of an immutable object).

但是,如果对象是可变的,则更改一个变量赋值的值自然会更改另一个变量赋值(例如a = b = []-> a.append(1),因此现在a和b将引用"[1]")

However, if an object is mutable, then changing the value in one variable assignment will naturally change the value in the other (e.g. a = b = [] -> a.append(1) so now both a and b will refer to "[1]")

与班级一起工作,似乎比我想象的还要流畅.我在下面写了一个简单的例子来说明不同之处.第一类是典型的Node类,带有一个下一个指针和一个值.设置两个变量"slow".节点对象("head")的相同实例与"fast"和"fast",然后改变两个"slow"和"fast"的值.和快速"不会影响对方.即,慢",快"和头"指的是慢",快"和头".都指向不同的对象(也通过检查其id()进行验证).

Working with classes, it seems even more fluid than I believed. I wrote a quick example below to show the differences. The first class is a typical Node class with a next pointer and a value. Setting two variables, "slow" and "fast", to the same instance of the Node object ("head"), and then changing the values of both "slow" and "fast" won't affect the other. That is, "slow", "fast", and "head" all refer to different objects (verified by checking their id() as well).

第二个示例类没有下一个指针,仅具有self.val属性.这次改变了两个变量之一"p1".两者都被设置为相同实例开始"的"p2"和"p2"将相互影响.尽管在开始"窗口中存在self.val.实例是一个不变的数字.

The second example class doesn't have a next pointer and only has a self.val attribute. This time changing one of the two variables, "p1" and "p2", both of which are set to the same instance, "start", will affect the other. This is despite that self.val in the "start" instance is an immutable number.

'''
The below will have two variable names (slow, fast) assigned to a head Node.
Changing one of them will NOT change the other reference as well.
'''

class Node:

    def __init__(self, x, next=None):
        self.x = x
        self.next = next

    def __str__(self):
        return str(self.x)

n3 = Node(3)
n2 = Node(2, n3)
n1 = Node(1, n2)
head = n1
slow = fast = head
print(f"Printing before moving...{head}, {slow}, {fast}")  # 1, 1, 1
while fast and fast.next:
    fast = fast.next.next
    slow = slow.next
    print(f"Printing after moving...{head}, {slow}, {fast}") # 1, 2, 3
    print(f"Checking the ids of each variable {id(head)}, {id(slow)}, {id(fast)}") # all different

'''
The below will have two variable names (p1, p2) assigned to a start Dummy.
Changing one of them will change the other reference as well.
'''

class Dummy:

    def __init__(self, val):
        self.val = val

    def __str__(self):
        return str(self.val)

start = Dummy(100)
p1 = p2 = start
print(f"Printing before changing {p1}, {p2}")  # 100, 100
p1.val = 42
print(f"Printing after changing {p1}, {p2}")   # 42, 42

这对于我了解幕后的实际情况有些晦涩,我正在寻求澄清,因此我可以自信地将多个变量赋值设置给同一对象,以期获得真实的副本(无需诉诸导入副本; copy.deepcopy(x);")

This is a bit murky for me to understand what is actually going on under the hood and I'm seeking clarification so I can feel confident in setting multiple variable assignments to the same object expecting a true copy (without resorting to "import copy; copy.deepcopy(x);")

谢谢您的帮助

推荐答案

这不是不变性还是可变性.这是使对象变异而不是重新分配引用的问题.

This isn't a matter of immutability vs mutability. This is a matter of mutating an object vs reassigning a reference.

如果该对象是不可变的,那么当我们将两个变量设置为同一对象时,它将是两个单独的副本

If that object is immutable then when we set two variables to the same object, it'll be two separate copies

这不是事实.无法复制.如果您有:

This isn't true. A copy won't be made. If you have:

a = 1
b = a

您有两个对同一个对象的引用,而不是该对象的副本.尽管因为整数是不可变的,但这很好.您不能变异 1 ,因此 a b 指向同一对象的事实不会对您造成任何伤害.

You have two references to the same object, not a copy of the object. This is fine though because integers are immutable. You can't mutate 1, so the fact that a and b are pointing to the same object won't hurt anything.

Python永远不会为您创建隐式副本.如果要复制,则需要自己明确地复制(使用 copy.copy 或其他方法,例如在列表上切片).如果您这样写:

Python will never make implicit copies for you. If you want a copy, you need to copy it yourself explicitly (using copy.copy, or some other method like slicing on lists). If you write this:

a = b = some_obj

a b 将指向同一个对象,无论类型为 some_obj ,还是不是可变的.

a and b will point to the same object, regardless of the type of some_obj and whether or not it's mutable.

那么您的示例之间有什么区别?

So what's the difference between your examples?

在您的第一个 Node 示例中,您实际上从未更改过任何 Node 对象.它们也可能是一成不变的.

In your first Node example, you never actually alter any Node objects. They may as well be immutable.

slow = fast = head

初始分配使两个 slow fast 指向同一对象: head .在那之后,您就可以这样做:

That initial assignment makes both slow an fast point to the same object: head. Right after that though, you do:

fast = fast.next.next

这会重新分配 fast 引用,但实际上不会改变 fast 正在查看的对象.您所要做的就是更改 fast 参考所关注的 what 对象.

This reassigns the fast reference, but never actually mutates the object fast is looking at. All you've done is change what object the fast reference is looking at.

但是,在第二个示例中,您直接对对象进行了突变:

In your second example however, you directly mutate the object:

p1.val = 42

虽然这看起来像是重新分配,但不是.这实际上是:

While this looks like reassignment, it isn't. This is actually:

p1.__setattr__("val", 42)

__ setattr __ 会更改对象的内部状态.

And __setattr__ alters the internal state of the object.

因此,重新分配会更改正在查看的对象.它将始终采用以下形式:

So, reassignment changes what object is being looked at. It will always take the form:

a = b  # Maybe chained as well.

与看起来像 的重新分配相反,但实际上是对象的变异方法的调用:

Contrast with these that look like reassignment, but are actually calls to mutating methods of the object:

l = [0]
l[0] = 5  # Actually l.__setitem__(0, 5)

d = Dummy()
d.val = 42  # Actually d.__setattr__("val", 42)

这篇关于了解Python中对类对象的可变性和多变量分配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆