Python中的可变对象与不可变对象

什么是可变对象/不可变对象:

可变对象:对象存放在地址中的值不会被改变(所谓的改变是创建了一块新的地址并把新的对象的值放在新地址中原来的对象并没有发生变化)

不可变对象:对象存放在地址中的值会原地改变

int str float tuple 都属于不可变对象 其中tuple有些特殊(下文解释) 

dict set list 属于可变对象

不可变对象实例:

性质1:

首先,看一下int类型:

a=1
b=1
c=a+0
print(a is b)
print(a is c)

is是对两个变量的id是否相等做出判断如果相等那么返回true 否则返回false

可以发现上述代码最终结果是打印了两个true

这里float str类型不再一一列出如果有兴趣可以自己调试一下发现 此类不可变变量有这样一个性质:

在除了tuple的不可变变量中,只要两个变量的数据类型相同并且值也相等,那么这两个变量的地址也相同

tuple是不符合这一性质的

a=(1,2,3)
b=(1,2,3)
print(a is b)

返回false 

这就是tuple相对于其他不可变变量的特殊性

上述不可变变量性质的优点在于减少了重复的值对内存空间的占用

性质2:

变量的不可变具体不可变在哪?

来看下面的代码:

i=73
print(id(i))
i+=2
print(id(i))

可以看出上面代码的运行结果a的值发生变化后地址也变了,工作原理如下图所示:

python 对象 可变类型 python可变对象与不可变对象_内存空间


需要注意的是i是一个变量,它指向对象的内容是73,当执行i+=2时,首先创建出一个新的内存里面存放改变后的值(75),然后让i指向新的地址,这是不可变对象在“改变”时的执行步骤,原来的对象内容和内存并没有发生变化

我们再来看一下字符串:

a="abc"
print(a.replace("a","A"))
print(a)

a执行replace 后把里面的“a”换成了“A“相当于创建的新的地址存放”Abc“ ,但是并没有再让a指向这块新的地址所以a并没有变化,通过这一性质我们可以推出:对于不变对象来说,调用对象自身的任意方法,也不会改变该对象自身的内容。相反,这些方法会创建新的对象并返回,这样,就保证了不可变对象本身永远是不可变的。

可变对象实例:

关于可变对象,是不满足上述的不可变对象性质一的而性质二刚好相反

可变对象对于自身的任意方法,是不需要重新开辟内存空间的,而是原地改变:

m=[5,9]
m.append(6)
print(m)

m的值变为了[5,9,6]当执行append时由于m是list类型属于可变对象所以它不会开辟新的内存空间而是像下图一样原地改变其值:

python 对象 可变类型 python可变对象与不可变对象_bc_02


注意:

当list用“+”来运算时会开辟新的内存空间

a1 = [1, 2, 3]
a2 = a1
print(id(a1), id(a2))
# 实际上是a2指向了新的对象,id已经改变。
# 所以现在a2、a1并不是同一对象的两个引用了,a2变化a1不会改变
a2 = a2 + [4] # 这个等式中,右边的a2还是和a1的id一样的,一旦赋值成功,a2就指向新的对象
print(id(1), id(a2))  # 不等,a2的id变化了
print(a1) # [1, 2, 3]没有变

如果是这样写:

a1 = [1, 2, 3]
a2 = a1
print(id(a1), id(a2))
a2 += [4]  # 相当于调用了a2.extend([4]),原地改变并没有新的对象产生
print(id(1), id(a2))  # 相等,a2的id没有变化
print(a1)



不同的地方在于a2 += [4],这句相当于调用了a2.extend([4])相当于原地改变,并没有新的对象产生。