1. 对象的赋值:
(1)代码:
A = ['nupt',56,['I','you','he']]
B = A # 把A赋值给B
# 比较A和B的值
print A
>>>['nupt',56,['I','you','he']]
print B
>>>['nupt',56,['I','you','he']] # B的值与A相同
# 比较A和B所在的地址
print id(A)
>>>58890248
print id(B)
>>>58890248 # B的地址与A相同
# 比较A中的每个元素与B中每个元素的地址
print [id(ele) for ele in A]
>>>[59288352L,57107912L,58888776L]
print [id(ele) for ele in B]
>>>[59288352L,57107912L,58888776L] # B中每个元素所在地址与A相同
# 更改A的值,看B的各项变化
A[0] = 'NanJing'
A[2].append('she')
# 比较A和B的值
print A
>>>['NanJing',56,['I','you','he','she']]
print B
>>>['NanJing',56,['I','you','he','she']] # B的值仍与A相同
# 比较A和B所在的地址
print id(A)
>>>58890248 # A的地址不变
print id(B)
>>>58890248 # B的地址仍与A相同
# 比较A中的每个元素与B中每个元素的地址
print [id(ele) for ele in A]
>>>[59285712L,57107912L,58888776L] # A[0]地址变了,A[2]没变
print [id(ele) for ele in B]
>>>[59285712L,57107912L,58888776L] # B中每个元素所在地址与A相同
(2)代码分析:
- 首先,创建了一个名为A的变量,这个变量指向一个list对象,变量A及其列表元素所存储的地址如下图:
- 然后,通过A向B赋值,那么B将指向A的对象(内存地址),即:B is A, B[ele] is A[ele],变量B及其列表元素的地址如下图:
- 接着,变量A的元素发生改变。
A[0]=’NanJing’,A[0]是str类型变量,str是不可变类型,所以修改时会产生一个新对象替换旧对象,也就相应产生一个新地址;
A[2].append(‘she’),A[2]是list类型变量,list是可变类型,修改时不会产生新对象,故list本身内存地址不变:
- 由于A和B指向同一个对象,所以对A的任何修改都会体现在B上:
下面来看看
2. 浅拷贝:copy.copy( )
(1)代码:
import copy # 导入copy模块
A = ['njupt',56,['I','you','he']]
B = copy.copy(A) # 把A浅拷贝给B
# 比较A和B的值
print A
>>>['njupt',56,['I','you','he']]
print B
>>>['njupt',56,['I','you','he']] # 浅拷贝的B,值与A相同
# 比较A和B所在的地址
print id(A)
>>>58890824
print id(B)
>>>59328584 # 浅拷贝的B,地址与A不同
# 比较A中的每个元素与B中每个元素的地址
print [id(ele) for ele in A]
>>>[59288312L,57107912L,58219080L]
print [id(ele) for ele in B]
>>>[59288312L,57107912L,58219080L] # 浅拷贝的B,元素所在地址与A相同
# 更改A的值,看B的各项变化
A[0] = 'NanJing'
A[2].append('she')
# 比较A和B的值
print A
>>>['NanJing',56,['I','you','he','she']]
print B
>>>['njupt',56,['I','you','he','she']] # 注意:浅拷贝的B,值与A不同
# 比较A和B所在的地址
print id(A)
>>>58890824 # A的地址不变
print id(B)
>>>59328584 # 浅拷贝的B,地址不变,仍与A不同
# 比较A中的每个元素与B中每个元素的地址
print [id(ele) for ele in A]
>>>[59286912L,57107912L,58219080L] # A[0]地址变了,A[2]没变
print [id(ele) for ele in B]
>>>[59288312L,57107912L,58219080L] # B中每个元素所在地址与A相同
(2)代码分析:
- 首先,创建了一个名为A的变量,这个变量指向一个list对象,变量A及其列表元素所存储的地址如下图:
- 然后,对A指向的对象进行浅拷贝,浅拷贝会创建一个新对象赋值给B,那么B指向的对象则不是A所指的对象,即:B is not A;但对于对象内部的元素,浅拷贝会使用原始元素的引用(内存地址),即:B[ele] is A[ele]
- 接着,变量A的元素发生改变。
A[0]=’NanJing’,A[0]是str类型变量,str是不可变类型,所以修改时会产生一个新对象替换旧对象,也就相应产生一个新地址;
A[2].append(‘she’),A[2]是list类型变量,list是可变类型,修改时不会产生新对象,故list本身内存地址不变:
- 由于B[2]和A[2]指向的同一地址,若存储在此地址的内容有变化,则A[2]和B[2]的内容皆会改变。因此当A[2]更新后(地址不变),B[2]的内容也随之改变;
而A[0]更新后,其旧对象被新对象替代,即新地址替代了旧地址,而B[0]指向的旧地址,其存储的内容并未改变,因此B[0]不变:
再来看看
3. 深拷贝copy.deepcopy( ):
(1)代码
import copy # 导入copy模块
A = ['nupt',56,['I','you','he']]
B = copy.deepcopy(A) # 把A浅拷贝给B
# 比较A和B的值
print A
>>>['nupt',56,['I','you','he']]
print B
>>>['nupt',56,['I','you','he']] # 深拷贝的B,值与A相同
# 比较A和B所在的地址
print id(A)
>>>58888776
print id(B)
>>>58890824 # 深拷贝的B,地址与A不同
# 比较A中的每个元素与B中每个元素的地址
print [id(ele) for ele in A]
>>>[59288312L,57107912L,58890248L]
print [id(ele) for ele in B]
>>>[59288312L,57107912L,59327880L]
# 深拷贝的B,元素所在地址与A不完全相同
# 更改A的值,看B的各项变化
A[0] = 'NanJing'
A[2].append('she')
# 比较A和B的值
print A
>>>['NanJing',56,['I','you','he' ]]
print B
>>>['njupt',56,['I','you','he']] # 注意:深拷贝的B,值与A不同
# 比较A和B所在的地址
print id(A)
>>>58888776 # A的地址不变
print id(B)
>>>58890824 # 深拷贝的B,地址不变,仍与A不同
# 比较A中的每个元素与B中每个元素的地址
print [id(ele) for ele in A]
>>>[59288592L,57107912L,58890248L] # A[0]地址变了,A[2]没变
print [id(ele) for ele in B]
>>>[59288312L,57107912L,59327880L] # B中每个元素所在地址不变
(2)代码分析
- 首先,创建了一个名为A的变量,这个变量指向一个list对象,变量A及其列表元素所存储的地址如下图:
- 然后,对A指向的对象进行深拷贝,深拷贝会创建一个新对象赋值给B,那么B指向的对象则不是A所指的对象,即:B is not A;
但是深拷贝跟浅拷贝的区别在于:对于对象中的元素,深拷贝都会重新生成一份(有特殊情况,下面会说明),而不是简单的使用原始元素的引用(内存地址)。
比如例子中,B[0] != A[0],B[1]=A[1],B[2]!=A[2]
- 接着,变量A的元素发生改变。
A[0]=’NanJing’,A[0]是str类型变量,str是不可变类型,所以修改时会产生一个新对象替换旧对象,也就相应产生一个新地址,id(A[0])改变
A[2].append(‘she’),A[2]是list类型变量,list是可变类型,修改时不会产生新对象,故list本身内存地址不变,即id(A[2])不变:
- 但是:由于B[0] != A[0],B[2]!=A[2],so A[0]A[2]的改变对B无影响
4. 注意:
Python中,类型属于对象。如a=23,变量a不是整数型,a仅仅是一个对象的引用(相当于指针),a可以指向list型,可以指向str型。
可变类型的对象:整数、字符串、元组。
如:变量赋值 a=5 后再赋值 a=10,这里实际是新生成一个 int 值对象 10,再让 a 指向它,而 5 被丢弃,不是改变a的值,而是相当于新生成一个a。不可变类型对象: 列表,字典。
如:变量赋值 b=[1,2,3,4] 后再赋值 b[2]=5 则是将 list b的第三个元素值更改,b本身没有动,只是其内部的一部分值被修改了
5. 结论:
- 对于不可变类型的对象:没有拷贝一说。如:浅拷贝/深拷贝时,B[0]=A[0],B[1]=A[1],A[0]A[1]改变,B[0]B[1]都不变
- 对于可变类型的对象:
(1)浅拷贝——指向原对象;原对象内容改变,拷贝对象内容也改变
(2)深拷贝——不指向研对象,重新生成新对象然后赋值给拷贝对象;原对象内容改变,拷贝对象内容不改变
(1)B=A:
B与A值一样,指向的地址一样,内部元素各自地址一样。即:B is A, B[ele] is A[ele]
A的一切元素变,B也变
(2)B=copy.copy(A)
B与A值一样,指向的地址不一样,内部各元素地址一样。即:B is not A, B[ele] is A[ele]
A的“可变类型对象”变,B也变
(3)B=deepcopy(A)
B与A值一样,指向的地址不一样,内部元素地址可能不一样。即:B is not A, B[ele] is not A[ele]
A的“可变类型对象”变,B不变