数据类型
不可变数据类型有:整形(int)、浮点型(float)、布尔型(bool)、字符串(str)、元组(tuple)
可变数据类型有:列表(list)、字典(dict)、集合(set)
用于区别不可变和可变数据类型的方法是通过修改该类型变量的值,观察是否会重新开辟新内存空间来判断可变和不可变,若会开辟新内存地址则为不可变数据类型,否则为可变数据类型
不可变数据类型
# 整形 int
a = 1
print(id(a))
a += 1
print(id(a))
print("*" * 50)
# 浮点型 float
a = 1.0
print(id(a))
a += 1
print(id(a))
print("*" * 50)
# 140730452513576
# 140730452513608
# **************************************************
# 1391888958064
# 1391871599024
# **************************************************
可以看到整形和浮点型在进行值的修改后,变量的内存地址均发生了变化,由于字符串和元组不支持修改,所以无法通过代码进行验证
可变数据类型
# 列表 list
a = [1, 2, 3]
print(id(a))
a.append(1)
print(id(a))
print("*" * 50)
# 字典 dict
a = {"name": "Mike", "age": 14}
print(id(a))
a["gender"] = "male"
print(id(a))
print("*" * 50)
# 集合 set
a = {1, 2, 3}
print(id(a))
a.add(4)
print(id(a))
# 1174038237184
# 1174038237184
# **************************************************
# 1173980009856
# 1173980009856
# **************************************************
# 1174037646464
# 1174037646464
可以看到在进行值的修改后,内存发生了变化,也就是重新开辟地址存放变量
补充:若实例化了一个自己定义的简单类的对象,此简单类也是默认为可变数据类型
class Simp:
def __init__(self, value1):
self.value1 = value1
if __name__ == "__main__":
a = 1
sim1 = Simp(1)
print(id(sim1))
sim1.value1 = 2
print(id(sim1))
# 2241894645392
# 2241894645392
引用
python中的内存可以看成分为了三大块,分别为栈内存、堆内存和代码块,变量名存储于栈内存,变量值,也就是变量引用的对象存储于堆内存,代码存于代码块中
引用关系可以理解为给被引用对象的别名,通过变量名调用被引用对象的值,python中最常见的引用就为“=”赋值符,在进行赋值操作的时候就是在进行引用。
比如a = 1,执行这个语句的时候,会在内存中创建一个不可变整形对象1,由a 引用 1
包括在函数传参的过程也是引用关系的建立
def exa(mya):
pass
if __name__ == '__main__':
a = 1
exa(a)
那么在上面这个简单的演示中,传参也就是做了一个mya = a的引用,也就是mya引用了1这个对象,那么这里就会出现若被引用的对象如果是可变数据类型,若改变mya的值,由于可变数据类型的性质,a的值也会更改
深拷贝与浅拷贝
python中深浅拷贝都是通过调用copy这个包来实现的
浅拷贝
浅拷贝,也就是copy.copy()
浅拷贝可以实现拷贝容器中第一层元素的拷贝,若第一层中存在可变数据类型则无法进行可变数据类型内部的拷贝
import copy
a = [1, 2, [3, 4]]
b = copy.copy(a)
print(id(a))
print(id(b))
print("*" * 50)
print(id(a[0]))
print(id(b[0]))
print("*" * 50)
print(id(a[2]))
print(id(b[2]))
# 2032482187840
# 2032482187328
# **************************************************
# 140730180735784
# 140730180735784
# **************************************************
# 2032481992064
# 2032481992064
可以看到a[2]和b[2]的地址相同,也就是对相同列表的引用,所以并没有实现整个列表的拷贝,我们可以通过修改内部列表的值进行验证
import copy
a = [1, 2, [3, 4]]
b = copy.copy(a)
a[2][0] = 5
print(a)
print(b)
# [1, 2, [5, 4]]
# [1, 2, [5, 4]]
可以看到a和b一起改变了值,只是由于列表中第三个元素也就是内部列表引用的缘故,若要实现多层拷贝可以通过深拷贝实现
深拷贝
深拷贝也就是copy.deepcopy()
深拷贝在进行拷贝的时候,若存在可变数据类型,则会深入内部再次进行拷贝,直到拷贝完整个对象
import copy
a = [1, 2, [3, 4]]
b = copy.deepcopy(a)
print(id(a))
print(id(b))
print("*" * 50)
print(id(a[2]))
print(id(b[2]))
a[2][0] = 5
print(a)
print(b)
# 1373953346688
# 1373953346176
# **************************************************
# 1373953118208
# 1373953346432
# [1, 2, [5, 4]]
# [1, 2, [3, 4]]
可以看到a[2]和b[2]的地址不同,并且对a进行修改并不会影响到b
深拷贝的应用举例
import copy
def exa(mya):
mya.append(4)
if __name__ == "__main__":
a = [1, 2, 3]
exa(a)
print(a)
# [1, 2, 3, 4]
可以看到在对列表对象进行传参的时候若在函数内部修改其值则会影响到本来的值,若不想产生这样的效果,就可以使用深拷贝
import copy
def exa(mya):
mya.append(4)
if __name__ == "__main__":
a = [1, 2, 3]
exa(copy.deepcopy(a))
print(a)
# [1, 2, 3]
这里传入函数内部的就是a的一个拷贝副本,函数只能获取到值,而不能对原来的a进行修改