Python中的可变和不可变对象以及赋值、浅拷贝和深拷贝对比分析
Python中的不可变对象和可变对象
在Python中,对象可以分为不可变(immutable)对象和可变(mutable)对象。这是关于对象在内存中如何管理和修改的概念。理解对象的可变性对于编写有效且可预测的Python代码非常重要。当您需要在函数之间传递参数或处理数据时,了解对象是否是不可变的或可变的将有助于避免意外副作用。不可变对象通常更容易推断其行为,因为它们不会在不经意间发生更改,而可变对象则需要更多的谨慎。
不可变对象(Immutable Objects)
不可变对象是指一旦创建,就不能被更改的对象。任何尝试修改不可变对象的值都将创建一个新的对象。常见的不可变对象包括整数(int)、浮点数(float)、字符串(str)、元组(tuple)等。不可变对象通常具有更高的内存效率,因为它们不需要在内存中保留额外的空间来处理修改。这也使得它们在哈希表等数据结构中很有用。
代码示例:
x = 5
y = x # y引用与x相同的不可变对象
x = 10 # 创建一个新的对象,x现在引用新的不可变对象,y仍然引用旧对象(5)
可变对象(Mutable Objects)
可变对象是指在创建后仍然可以修改其值的对象。修改操作不会创建新的对象,而是直接在原对象上进行更改。常见的可变对象包括列表(list)、字典(dict)、集合(set)等。可变对象的修改可能需要更多的内存和处理时间,因为它们允许在原地修改,而不是创建新的对象。
代码示例:
my_list = [1, 2, 3]
my_list.append(4) # 直接在原列表上添加元素
# my_list仍然引用同一个可变对象,其值现在为[1, 2, 3, 4]
Python中的赋值、浅拷贝和深拷贝对比分析
不可变对象(Immutable Objects)
Python 内不可变对象的内存管理方式是引用计数,所以 无论是赋值、前拷贝还是深拷贝,Python 不会对值相同的不可变对象,申请单独的内存空间,只会记录它的引用次数。
a = "marcos"
b = a
c = copy.copy(a)
d = copy.deepcopy(a)
print("原值:%d" % id(a))
print("赋值:%d" % id(b))
print("浅拷贝:%d" % id(c))
print("深拷贝:%d" % id(d))
输出:
原值:1885360472840
赋值:1885360472840
浅拷贝:1885360472840
深拷贝:1885360472840
但对不可变对象进行修改时,实际上是让变量指向了一个新的对象(对象的地址重新分配)。
可变对象(Mutable Objects)
赋值是引用对象的地址,不会分配新的地址,浅拷贝和深拷贝均会为复制对象分配新的内存空间。
但是,对于可变对象内的可变元素和不可变元素,发生浅拷贝和深拷贝时存在区别:
(1)当对原对象浅拷贝时,原对象内可变对象的对应元素的改变,复制对象的对应元素也相应改变,而不可变元素不会随着原对象元素的改变而改变;
(2)当对原对象深拷贝时,无论原对象的元素如何发生变化,复制对象的对应元素均不发生变化。
import copy
a = [[0],"zhangsan"]
b = a
c = copy.copy(a)
d = copy.deepcopy(a)
print("原值地址:%d" % id(a))
print("赋值地址:%d" % id(b))
print("浅拷贝地址:%d" % id(c))
print("深拷贝地址:%d" % id(d))
a[0][0] = 1
a[1] = "lisi"
print("原值:",a)
print("赋值:",b)
print("浅拷贝:",c)
print("深拷贝:",d)
print("原值地址:%d" % id(a))
print("赋值地址:%d" % id(b))
print("浅拷贝地址:%d" % id(c))
print("深拷贝地址:%d" % id(d))
输出:
原值地址:1724350537608
赋值地址:1724350537608
浅拷贝地址:1724350569544
深拷贝地址:1724350587336
原值: [[1], 'lisi']
赋值: [[1], 'lisi']
浅拷贝: [[1], 'zhangsan']
深拷贝: [[0], 'zhangsan']
原值地址:1724350537608
赋值地址:1724350537608
浅拷贝地址:1724350569544
深拷贝地址:1724350587336
附:更多的示例
clone函数
下列程序,只是把x的索引赋值给y,并没有给y分配内存空间,当改变y时,x也发生改变。
x = torch.arange(6).reshape((2,3))
y = x
y[:] = 2
下列程序,为y分配了内存空间,改变y时并不改变x的值。
x = torch.arange(6).reshape((2,3))
y = x.clone()
y[:] = 2