概念说明
这里的直接赋值、浅拷贝和深度拷贝的对象都是列表或者是字典这样的复杂数据类型,像是整数或是字符串这样的基本数据类型不属于直接赋值等的范畴
- 直接赋值: 为对象取别名
- 浅拷贝(copy): 拷贝父对象,不会拷贝对象内部的子对象
- 深拷贝(deepcopy): copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象。
直接赋值
-
如果是复杂数据类型变量(list,dict)的赋值操作视为为变量取别名,简单来说(以列表为例),列表
a=[1,2,3] b = a
,经过这样操作之后b实际上是数组a的别名,可以理解为b和a都是指向数组[1,2,3]
的指针(python中没有指针操作),复制操作仅仅是将指针又重新复制了一份命名为ba = [1,2,3] # b是a的直接赋值 b = a b.append(5) print("id(a):",id(a)) print("id(b):",id(b)) print("id(id(a))",id(id(a))) print("id(id(b))",id(id(b))) print("a",a) print("b",b)
输出结果如下:
id(a): 2656351178952 id(b): 2656351178952 id(id(a)) 2656384180144 id(id(b)) 2656384180176 a [1, 2, 3, 5] b [1, 2, 3, 5]
id()用来取地址,上面的输出可以看出a和b代表的是同一块内存地址,并且当b修改时,a也随之修改
-
如果是基本数据类型的变量赋值是创建一个新的副本
a = "string" b = a b += "78" print("a:",a) print("b:",b) print(id(a)) print(id(b))
结果如下:
a: string b: string78 2840820288456 2840827133872
可以看到a和b是在不同的内存中,b的修改不会影响a的值
浅拷贝
a = [1,[1,2,3]]
# b是a的浅拷贝
b = a.copy()
print("id(a):",id(a))
print("id(b):",id(b))
b[0]=4
print("a:",a)
print("b:",b)
print("id(a[1])", id(a[1]))
print("id(b[1])", id(b[1]))
b[1].append(5)
print("a:", a)
print("b:", b)
输出:
id(a): 1493887745032
id(b): 1493887822536
# a和b的地址不同,说明a和b有属于各自的内存分配
a: [1, [1, 2, 3]]
b: [4, [1, 2, 3]]
# 修改b中的简单数据类型对象,a不变
id(a[0]) 1992059968
id(b[0]) 1992060064
# b和a中的基本数据类型对象地址不同,二者独立存在
id(a[1]) 1493846348168
id(b[1]) 1493846348168
# b和a中的复杂数据类型对象的地址相同说明二者的复杂数据类型是指向了同一片空间
a: [1, [1, 2, 3, 5]]
b: [4, [1, 2, 3, 5]]
# 修改后得到佐证
上面的例子说明,复杂数据类型的浅拷贝完全复制了内存中的简单数据类型,对于复杂的数据类型使用的是储存了一个别名,具体如下:
#浅拷贝操作等价于这样
a = [1,[1,2,3]]
a0=a[0]
a1=a[1]
b = []
b.append(a0)
b.append(a1)
如果是使用上面的输出可得到相同的结果
id(a): 2476418727368
id(b): 2476395376072
a: [1, [1, 2, 3]]
b: [4, [1, 2, 3]]
id(a[0]) 1992059968
id(b[0]) 1992060064
id(a[1]) 2476368929544
id(b[1]) 2476368929544
a: [1, [1, 2, 3, 5]]
b: [4, [1, 2, 3, 5]]
深度拷贝
深度拷贝使用copy模块的deepcoy方法,将数据完全复制一份放到另一块内存中,不论是否复杂数据类型变量中含有列表或字典
from copy import deepcopy
a = [1,[1,2,3]]
b = deepcopy(a)
print("id(a):",id(a))
print("id(b):",id(b))
b[0]=4
print("a:",a)
print("b:",b)
print("id(a[0])", id(a[0]))
print("id(b[0])", id(b[0]))
print("id(a[1])", id(a[1]))
print("id(b[1])", id(b[1]))
b[1].append(5)
print("a:", a)
print("b:", b)
'''
b = deepcopy(a)
等价于
b = []
a0 = a[0]
a1 = deepcopy(a[1])
b.append(a0)
b.append(a1)
'''
输出如下:
id(a): 1613491154632
id(b): 1613491213576
a: [1, [1, 2, 3]]
b: [4, [1, 2, 3]]
id(a[0]) 1992059968
id(b[0]) 1992060064
id(a[1]) 1613475312712
id(b[1]) 1613446907144
# a[1]和b[1]的地址不同指向不同的内存
a: [1, [1, 2, 3]]
b: [4, [1, 2, 3, 5]]
上面三部分参考自:(菜鸟教程)Python 直接赋值、浅拷贝和深度拷贝解析
Python中读取指定内存地址的值
- 在C/C++中我们有
&
取地址操作,使用*
进行输出地址的值操作 - 在python中我们使用
id()
函数进行取地址操作,但是没有内置函数用来输出地址的值,在这里使用ctypes模块实现类似的功能
import ctypes
values='hello world' #定义一个字符串字面量
address=id(value) #获取value地址,赋值给address
print(address)
get_value = ctypes.cast(address,ctypes.py_object).value #读取地址中的变量
print(get_value)
输出
2026299085168
hello world
以上部分来自链接:Python通过地址获取变量
ctypes模块
ctypes
模块是用来在python中使用C数据类型的模块
NAME
ctypes - create and manipulate C data types in Python
在ctypes模块中的cast函数说明(貌似有些草率)
cast(obj, typ)