1、概述

python中涉及到可变对象的赋值操作,整理的浅拷贝和深拷贝相关的内容。

2、一些基本的概念

python变量的赋值和java、c++等有些不同。java和c++在定义变量时候,就指定了变量的类型,如果对该变量赋值其他类型,则会报错。而python是通过对变量地址的引用,来进行赋值的,类型由被指向的变量决定。

1.声明一个变量名,就生成一个系统变量表;
2.声明的时候赋值,就是变量名对值的地址的引用,同时在python的系统类型中确认的变量类型;
3.python的类型只与对象有关,与变量无关;
4.python的变量的申明是动态类型,可以动态指向不同的值,可以通过id函数来查看变量存储的内存

2.1 不可变对象

不可变对象为一些字面值的常量,字符串类型,如字符串 "python" 数字型:int类型,float类型等,如有数字10 100,3.14小数。
对于字符串来说,是不支持原位改变的。

  >>> s1 = 'python'
>>> s1[0] = 'j'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> s1.append('3')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'append'

img
python对于常用的整数,是会有一个整数池,整数都具有相同的地址。
img

2.2 可变对象

对象的值本身是可以被改变的,如list dict tuple 都是可变对象,tuple比较特殊不支持原位改变,但是可以追加。

2.2.1 直接进行变量的赋值

img

a = [0,1,2,3,4,5,6,7,8,9]
b = a
print(f'id(a):{id(a)} id(b):{id(b)}')
# 两个变量的地址相同
id(a):2407201034944 id(b):2407201034944

如果修改代码
img
如果涉及到dict类型有相同的现象。

d ={i:i**2 for i in range(0,11)}
print(d)
d1 = d
print(f'd1 is d:{d1 is d}')
d[0] = 'a'
print(f'修改d后\nd is {d} \nd1 is {d1}')
d1['aaa'] = 'ddd'
print(f'修改d1后\nd is {d} \nd1 is {d1}')
输出:--------------------------------------------------------------------------------------------
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}
d1 is d:True
修改d后
d is {0: 'a', 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100} 
d1 is {0: 'a', 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}
修改d1后
d is {0: 'a', 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100, 'aaa': 'ddd'} 
d1 is {0: 'a', 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100, 'aaa': 'ddd'}

img

对于tuple有点不同,追加元素,实际是指向了一个新的tuple
img

t = (1,2,3,4,5,6,7,8,9)
t0 = t
t += ('a','b')
print(t,t0)
t0 += ('c','d')
print(t,t0)
输出:-----------------------------------
赋值后t0和t地址是否相同:True
修改t的值后
t:(1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b')
t0:(1, 2, 3, 4, 5, 6, 7, 8, 9)
修改t0的值后
t:(1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b')
t0:(1, 2, 3, 4, 5, 6, 7, 8, 9, 'c', 'd')

3、可变对象嵌套

以上为简单的场景,每个可变对象内没有再嵌套可变对象。而对于可变对象的嵌套,如果进行赋值,情况则有些不同。

3.1 引用

img
此时,a和b指向的是相同的list的地址,更改a或者b中任意一个,都会同时修改到a b的值。

a = [0,1,2,3,4,5,6,7,['a','b','c',[11,12,13]],8,9]
b = a
a[8].append('d')
b[8][3].append(14)
print(f'a is:{a}')
print(f'b is:{b}')
输出:
a is:[0, 1, 2, 3, 4, 5, 6, 7, ['a', 'b', 'c', [11, 12, 13, 14], 'd'], 8, 9]
b is:[0, 1, 2, 3, 4, 5, 6, 7, ['a', 'b', 'c', [11, 12, 13, 14], 'd'], 8, 9]

img

3.2 浅拷贝

浅拷贝可以通过以下几种方式

a = [0,1,2,3,4,5,6,7,['a','b','c',[11,12,13]],8,9]
# 切片的方式是浅拷贝
b = a[:]
# 使用可变对象copy方法
c = a.copy()
# 通过copy模块进行浅拷贝
import copy
d = copy.copy(a)

img

a = [0,1,2,3,4,5,6,7,['a','b','c',[11,12,13]],8,9]
b = a[:]
print(f'b从a进行浅拷贝,b和a的值相同:{a is b}')
print(f'嵌套的第二层列表是否相同:{a[8] is b[8]}')
print(f'嵌套的第三层列表是否相同:{a[8][3] is b[8][3]}')
输出:-------------------------------------------------------
b从a进行浅拷贝,b和a的值相同:False
嵌套的第二层列表是否相同:True
嵌套的第三层列表是否相同:True

浅拷贝,可变对象中又引用的对象,如list引用的list,浅拷贝至始至终都是一样的,修改这部分值会导致所有浅拷贝后的变量的值同步被修改。
浅拷贝的一个例子:

a = [[1,2,3]]
b = a*3
b[0][0] = 20
print(b)
输出:
[[20, 2, 3], [20, 2, 3], [20, 2, 3]]

a*3指的是将列表中的元素重复3次,然后追加到末尾,这里是浅拷贝,b中的3个元素都是指定同一个list。
img

3.3 深拷贝

深拷贝将对象完全复制一部分,原始对象和深拷贝后的对象完全没有联系。

import copy
a = [0,1,2,3,4,5,6,7,['a','b','c',[11,12,13]],8,9]
# 切片的方式是浅拷贝
b = copy.deepcopy(a)
a.append(10)
a[8].append('d')
a[8][3].append(14)
print(f'a 修改后\na:{a}\nb:{b}')

b.append('x')
b[8].append('y')
b[8][3].append(999)
print(f'b 修改后\na:{a}\nb:{b}')
输出:

a 修改后
a:[0, 1, 2, 3, 4, 5, 6, 7, ['a', 'b', 'c', [11, 12, 13, 14], 'd'], 8, 9, 10]
b:[0, 1, 2, 3, 4, 5, 6, 7, ['a', 'b', 'c', [11, 12, 13]], 8, 9]
b 修改后
a:[0, 1, 2, 3, 4, 5, 6, 7, ['a', 'b', 'c', [11, 12, 13, 14], 'd'], 8, 9, 10]
b:[0, 1, 2, 3, 4, 5, 6, 7, ['a', 'b', 'c', [11, 12, 13, 999], 'y'], 8, 9, 'x']

img

posted on 2022-07-06 10:11  飞飞fly  阅读(1191)  评论(0)    收藏  举报