内容概要
操作 | 内存分析 | 变更效果 | 详细说明 |
---|---|---|---|
赋值 (=) | 两个变量指向同一内存地址。 | 对变量所指向内存地址的内容做任何变动,另一变量均与其发生一样的变化。 | 除非变量所指向的内存地址发生变化(即重新赋值),否则两个变量可以视为同一个。 |
浅拷贝 (copy) | 两个变量指向不同内存地址; 两个变量存储的内容中,无论是变量还是常量,指向的内存地址或分配的内存地址均一致。 | 如果更换首层元素中的子变量,变更内容仅在自身发生; 如果对首层元素中的子变量所存储的内容进行变更,拷贝与被拷贝的变量均发生变更; 如果对首层元素中的常量进行变更,变更内容仅在自身发生。 | 通过浅拷贝将原变量指向的内容拷贝出一份,放到新的内存空间中。 更换子变量:直接变更变量中指向的地址,即指向新子变量的地址,所以拷贝出来的变量不受影响; 子变量内容变更:由于拷贝与被拷贝的变量指向的子变量地址相同,其指向的子变量内容发生变更时,两者均会发生相同的变更; 常量变更:变更常量实质是变更指向的地址,故仅在变量自身中发生变动,拷贝出的变量不受影响。 |
深拷贝 (deepcopy) | 两个变量指向不同内存地址; 两个变量存储的内容中,如果是常量则存储的地址一致,如果是变量则指向的地址不同。 | 任一变量发生任何变动,另一变量均不受影响。 | 通过深拷贝,逐层将变量中的内容均拷贝出来放在新的内存空间,故所有层次的变量指向的地址均与原变量不同,故对变量所指向内容的变动不影响深拷贝出的变量; 在双方变量中引用的常量存储在同一内容地址中,但由于常量变更即是变更了指向对象,也是属于变量内容的变更,而常量本身没有发生变化,因此也不会互相影响。 |
代码分析
1. 静态比较
通过对内存地址或指向地址的分析,了解深浅拷贝与赋值操作是如何影响变量的:
-
通过各种方式使变量获取相同的内容
- a = [[3], 1, 2]
- b = a.copy()
- c = copy.deepcopy(a)
- d = a
-
查看各变量所指向的内存地址,
通过拷贝获取内容的变量,其指向的内存地址与原变量所指向的不同。
- a: 38108400;
- b: 38108792;
- c: 38108512;
- d: 38108400.
-
比较存储的变量所指向的内存地址,
通过深拷贝获取的子变量,其所指向的内存地址也不同
- 浅拷贝:a: 38108736, b: 38108736
- 深拷贝:a: 38108736, c: 38108568
- 赋值:a: 38108736, d: 38108736
-
比较存储的常量所分配的内存地址,
所有常量的存储地址均相同
- 浅拷贝:a: 8791402687248, b: 8791402687248
- 深拷贝:a: 8791402687248, c: 8791402687248
- 赋值:a: 8791402687248, d: 8791402687248
参考代码如下:
import copy
a = [[3], 1, 2]
b = a.copy()
c = copy.deepcopy(a)
d = a
print('通过各种方式使变量获取相同的内容')
print('a: {aa};\nb: {bb};\nc: {cc};\nd: {dd}.'.format(**{'aa': a, 'bb': b, 'cc': c, 'dd': d}))
print('查看各变量所指向的内存地址')
print('a: {aa};\nb: {bb};\nc: {cc};\nd: {dd}.'.format(**{'aa': id(a), 'bb': id(b), 'cc': id(c), 'dd': id(d)}))
print('比较存储的变量所指向的内存地址')
print('浅拷贝:a: %s, b: %s' % (id(a[0]), id(b[0])))
print('深拷贝:a: %s, c: %s' % (id(a[0]), id(c[0])))
print('赋值:a: %s, d: %s' % (id(a[0]), id(d[0])))
print('比较存储的常量所分配的内存地址')
print('浅拷贝:a: %s, b: %s' % (id(a[1]), id(b[1])))
print('深拷贝:a: %s, c: %s' % (id(a[1]), id(c[1])))
print('赋值:a: %s, d: %s' % (id(a[1]), id(d[1])))
2. 动态比较
通过对变量和常量进行变更,了解深浅拷贝与赋值操作是如何影响变量的:
-
变更所存储的变量,
仅通过“赋值操作”获得的变量会随原变量一起变更。
- 原内容: [[3], 1, 2]
- 执行动作:替换原列表,a[0] = [9, 9]
- 变更后:
- 浅拷贝:a: [[9, 9], 1, 2], b: [[3], 1, 2]
- 深拷贝:a: [[9, 9], 1, 2], c: [[3], 1, 2]
- 赋值:a: [[9, 9], 1, 2], d: [[9, 9], 1, 2]
-
变更子变量的内容,
通过“浅拷贝”、“赋值操作”获得的变量均会随原变量一起变更,深拷贝获得的变量不受影响。
- 原内容: [[3], 1, 2]
- 执行动作:增加原列表内容,a[0].append(4)
- 变更后:
- 浅拷贝:a: [[3, 4], 1, 2], b: [[3, 4], 1, 2]
- 深拷贝:a: [[3, 4], 1, 2], c: [[3], 1, 2]
- 赋值:a: [[3, 4], 1, 2], d: [[3, 4], 1, 2]
-
变更所存储常量的内容,
仅通过“赋值操作”获得变量会随原变量一起变更。
- 原内容: [[3], 1, 2]
- 执行动作:替换原常量,a[1] = 0
- 变更后:
- 浅拷贝:a: [[3], 0, 2], b: [[3], 1, 2]
- 深拷贝:a: [[3], 0, 2], c: [[3], 1, 2]
- 赋值:a: [[3], 0, 2], d: [[3], 0, 2]
参考代码如下:
import copy
print('变更所存储的变量')
a = [[3], 1, 2]
b = a.copy()
c = copy.deepcopy(a)
d = a
print('原内容:', a)
a[0] = [9, 9]
print('变更后:')
print('浅拷贝:a: %s, b: %s' % (a, b))
print('深拷贝:a: %s, c: %s' % (a, c))
print('赋值:a: %s, d: %s' % (a, d))
print('变更子变量的内容')
a = [[3], 1, 2]
b = a.copy()
c = copy.deepcopy(a)
d = a
print('原内容:', a)
a[0].append(4)
print('变更后:')
print('浅拷贝:a: %s, b: %s' % (a, b))
print('深拷贝:a: %s, c: %s' % (a, c))
print('赋值:a: %s, d: %s' % (a, d))
print('变更所存储常量的内容')
a = [[3], 1, 2]
b = a.copy()
c = copy.deepcopy(a)
d = a
print('原内容:', a)
a[1] = 0
print('变更后:')
print('浅拷贝:a: %s, b: %s' % (a, b))
print('深拷贝:a: %s, c: %s' % (a, c))
print('赋值:a: %s, d: %s' % (a, d))