Python走过的坑,可变不可变数据类型
- Python标准数据类型
- Python不可变数据类型(一个萝卜一个坑)
- 可变数据类型(一块地好多萝卜)
Python标准数据类型
- Number(数字)
- String(字符串)
- Dictionary(字典)
- List(列表)
- Tuple(元组)
- Set(集合)
Python不可变数据类型(一个萝卜一个坑)
比如数字,字符串,元组。这是什么意思呢,show your code:
a = 1
print("变量a在内存当中的地址":,id(a))
a += 1
print("变量a在内存当中的地址":,id(a))
结果为
变量a在内存当中的地址 1683713088
变量a在内存当中的地址 1683713120
可以看到,变量a在内存当中的地址发生了变化,多了32个bit,也就是4个字节,这里说句题外话,在python中int代表的是长整型,没有 python2 中的 Long。
换句话说,一个数字对应一个内存地址,数字变,地址变,一个萝卜一个坑。
还有一个要注意的地方。
a = 1
print("变量a在内存当中的地址":,id(a))
b = 1
print("变量a在内存当中的地址":,id(b))
结果为:
变量a在内存当中的地址 1683713088
变量a在内存当中的地址 1683713088
一模一样的,这说明了什么,说明了变量a和b指向了同一块内容,其实就是同一个对象的不同引用,也就是同一个人的不同名字而已。或者还是拿萝卜来说。一个坑里,只能长一个萝卜,中国人叫萝卜,老外管它叫turnip(萝卜的英文),这下懂了吧。
可变数据类型(一块地好多萝卜)
典型的就是列表,这个可吃了不少亏。
来吧
a = [1,2,3]
print("变量a在内存当中的地址":,id(a))
a.append(4)
print("变量a在内存当中的地址":,id(a))
结果为
变量a在内存当中的地址 2635029663240
变量a在内存当中的地址 2635029663240
可以看到,一毛一样啊。这就相当于在原来的地里又长了一个萝卜,当然也许是又长了白菜,土豆什么的内建的数据类型,或者是自定义的数据类型等等。
比如a = [1,2,3,“白菜”]
来一张图更形象一点。一个矩形的土地,长了3个萝卜。
然后又扔过去一个白菜。
地还是那块地(内存地址并没有发生改变),但是其中元素增加了。
总结为:元素变,地址不变,同一块地可以种好多萝卜,这就是可变数据类型。
这里也有一个值得注意的问题:
come on code!
a = [1,2,[3,4]]
print('a = :',a)
b = a
b[2] = [5,6]
print('a = ',a)
结果为:
a = [1, 2,[3, 4]]
a = [1, 2, [5, 6]]
这里a和b所指的是同一个东西,也就是说,a和b都是[1,2,[3,4]]这个列表的引用,也就是外号。一般我们在内存中创建了一个列表,有两种情况:
- 需要创建一个此列表的副本,一个一模一样的列表,只不过在内存中多开辟了一块空间来。这样做的好处是,对副本所做修改不会影响到原来的列表,因为有时候我们需要原列表与修改后的列表进行对比。需要创建一个此列表的副本,一个一模一样的列表,只不过在内存中多开辟了一块空间来。这样做的好处是,对副本所做修改不会影响到原来的列表,因为有时候我们需要原列表与修改后的列表进行对比。比如:
a = [1,2,[3,4]]
b = a.copy()
a[2][-1] = 'x'
print(a)#结果为[1, 2, [3, 'x']]
print(b)#结果为[1, 2, [3, 'x']]
咦,不对呀,我明明创建了一个原列表的副本啊,怎么对a修改,b也会跟着修改呢?这是因为浅复制,深复制的问题,如果列表只有一层,那么用浅复制足够了。但是我们做项目的时候,动不动就好几层的list,因此,必须采用深复制。下面是实例代码:
import copy
a = [1,2,[3,4]]
b = copy.deepcopy(a)
a[2][-1] = 'x'
print(a)#结果为[1, 2, [3, 'x']]
print(b)#结果为[1, 2, [3, 4]]
这样,就可以完完全全地复制一个列表了。有个博客对这种情况说的挺好的, 有句话是这么说的,因为浅复制 ,复杂子对象的保存方式是作为引用方式存储的,所以修改浅复制的值和原来的值都可以改变复杂子对象的值。
2. 只需另外再创建一个对列表对象的引用即可,这种情况不需要原列表与修改后的列表进行对比,当列表比较大,又使用不到以前版本的列表时,推荐使用这种方式,节省内存。比如
a = [1,2,3]
b = a