Hello NanFeng
可变数据与不可变数据
之前有经常提到数据的可变与不可变,今天就稍作总结。
按照可变与不可变的分类,其中:
数值类型、字符串和元组是不可变数据类型
列表、字典和集合是可变数据类型
什么是可变与不可变数据
在说之前,先简单介绍下python的变量存储与赋值的机制。
这里用Linux的虚拟环境下的python3演示:
>>> str1 = "hello nan feng"
>>> str1
'hello nan feng'
>>> id(str1)
139894069434160
>>> list1 = [1, 2, 3]
>>> list1
[1, 2, 3]
>>> id(list1)
139894069415368
python在创建变量的时候会先在内存开辟一块区域用来存储要赋的值,然后把存储该值的地址调用存储到变量里,所以在python里变量存储的只是一个地址信息,并不是实际的值,这种巧妙地方式能够使变量的大小一致,便于存储。具象理解可以看下图:
可变数据类型(mutable):
这类数据类型的变量发生值的变化(发生值的改变,append()以及+=等操作),内存地址也不会改变(重新赋值地址会发生改变)
就是说该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的地址,改变的还是这个变量本身
接下来都使用pycharm集成开发环境演示:
#赋值
list_1 = [1, 2, 3]
print(id(list_1))
#list的append的方法
list_1.append(2)
print(id(list_1))
#list的切片赋值
list_1[0] = 2
print(id(list_1))
##输出
2283983488136
2283983488136
2283983488136
可以看到前三个改变值的方法并不会改变变量所指的值的地址。
不可变数据类型
:这类数据类型的变量发生值的变化,内存地址会改变
就是说该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把变化后的值赋给了变量,这会开辟一个新的地址,同时变量再指向这个新的地址
num_1 = 123
print(id(num_1))
num_1 += 1
print(id(num_1))
##输出
1479721856
1479721888
可以看到数值类型更新了值后,也发生了内存地址的改变
但有时候我们会发现可变数据在某种情况下改变了值会发生地址的改变,例如:
#赋值
list_1 = [1, 2, 3]
print(id(list_1))
#重新创建一个变量赋值同样的值
list_2 = [1, 2, 3]
print(id(list_2))
##输出
2123681798728
2123681798664
其实,第二部本质上是重新赋值,并不是改变值,不管是可变还是不可变,重新赋值肯定会改变指向的存储地址,只是对于可变数据,存在不改变地址的更新值的方法。
几个问题
这里是我之前总结的时候想到过的问题,大概是关于不同变量同一个值,大家有其他有趣的问题也可分享一下。
- 赋值一个变量后,用第一个变量赋值第二个变量,二者的地址指向是否相同?变化是否同步?
- 不同变量赋同一个值,指向的内存地址是否相同?
重新赋值
list1 = [1, 2, 3]
print(id(list1))
# 让另一个变量等于另一个变量
list2 = list1
print(id(list2), list2)
# 给第一个变量重新赋值,第二个变量不受影响
list1 = [4, 5, 6]
print(id(list1))
print(id(list2), list2)
##输出
2257855158856
2257855158856 [1, 2, 3]
2257855158792
2257855158856 [1, 2, 3]
append方法
list1 = [1, 2, 3]
print(id(list1))
list2 = list1
print(id(list2), list2)
# 用append()更新值后,后者的内容同步发生变化,二者指向的内存地址都不变
list1.append(4)
print(id(list1))
print(id(list2), list2)
##输出
1375908502088
1375908502088 [1, 2, 3]
1375908502088
1375908502088 [1, 2, 3, 4]
可以得到
当让另一个变量等于另一个变量时,二者指向同一内存地址,重新将一个变量赋值后,另一个变量任然不变,当使用append方法时,二者同步变化。究其本质,还是因为二者指向同一个内存地址,重新赋值的变量会开辟新的内存地址,放弃原来的调用,存储新的调用。
这里要提一下:当一个内存地址没有被任何变量指向时,该内存地址便会被释放。
不同变量相同值
list1 = [1, 2, 3]
list2 = [1, 2, 3]
print(id(list1), id(list2))
##输出
2105520200264 2105520200200
可以看到,即便两个变量赋的是相同的值,内存地址也是不一样的。
但需要注意的是,在pycharm集成环境中,有时候会莫名出现上述操作输出相同的内存地址的情况,从理论上来说,二者的内存地址绝对是不一样的,多次测试发现,这种情况貌似只在pycharm中存在,在windows的cmd命令行和linux安装的python3环境下都不会存在,这里暂时不知道是什么原因,但影响不大,感兴趣的伙伴可以多试试。
Hello NanFeng