问题
python使用 等号(=)对变量赋值时,更改这个变量值,原来的变量的值也被改变了,这是为什么👀️ 👀️ 👀️
示例
列表(list)
a = [1,2,3,4,5]
b = a
b[-1] = 999
print(a)
# 运行结果 ---------------
[1, 2, 3, 4, 999]
字典(dict)
a = {'t1':[1,2,'33'], 't2': 'abcd'}
b = a
b['t1'] = [0]
b['t2'] += '1234'
print(a)
# 运行结果 ------------
{'t1': [0], 't2': 'abcd1234'}
字符串(str)
a = 'python'
b = a
b = a + 'mysql'
print(a)
# 运行结果 -------------------
python
整数、浮点值(int、float)
a = 9
b = a
b += 1
print(a)
a1 = 3.1415926
b2 = a1
b2 = 10 - 0
print(a1)
# 运行结果 ---------------
9
3.1415926
元组(tuple)
tup1 = (12, 34.56)
tup2 = tup1
# 其实这个地方并没有对tup2做修改,而是创建了一个新元组,因为元组是不可变的(为了凑数把元组也写上了)
tup2 = tup2 + (99,)
print(tup1)
# 运行结果 ----------------
(12, 34.56)
原因——变量的传递规则,是引用还是拷贝
Python 赋值过程中不明确区分引用和拷贝,一般对静态变量的传递为拷贝,对动态变量的传递为引用。(注,对静态变量首次传递时也是引用,当需要修改静态变量时,因为静态变量不能改变,所以需要生成一个新的空间存储数据)。
- 列表,字典为动态变量。(传递变量是就是引用)
- 字符串,数值,元组均为静态变量。(传递变量是就是拷贝)
所以上面 列表和字典示例中,原始变量都被改变了,而字符串、数值、元组的原始变量却没有被改变
变量有时比较复杂,存在组合现象,如果实在不清楚状况,可以用id()这个函数查看变量的内存地址,如果是引用,两个变量指向的内存地址是相同的。例如:
a = 6
b = a
print(id(a))
print(id(b))
b = b+4
print(id(b))
# 运行结果 -------------------
140722262968128
140722262968128
140722262968256
可以看到每个变量都有一个内存地址,修改变量b之前,a和b指向的内存地址是相同的,修改b后,内存地址就变了
解决
如果我们想要用变量对变量赋值,但是又不想原始变量被改变,那怎么解决呢?
浅拷贝
copy --- 浅层及深层拷贝操作 — Python 3.12.6 文档
列表和字典都有一个copy()方法,还有copy模块的copy.copy()方法,这两个都属于浅拷贝
import copy
# 定义一个包含嵌套列表的列表
lst1 = [1, 2, [3, 4]]
# 使用列表的copy()方法进行浅拷贝
lst2 = lst1.copy()
# 使用copy模块的copy.copy()方法进行浅拷贝
lst3 = copy.copy(lst1)
# 修改原始列表中的嵌套列表
lst1[2][0] = 999
# 打印结果,可以看到lst2和lst3的嵌套列表也被修改了,三个变量的内存地址都不一样,但嵌套列表的地址都是一样的说明嵌套列表的值是引用而不是拷贝
print(lst1, id(lst1), id(lst1[2])) # [1, 2, [999, 4]] 2560120329792 2560120332096
print(lst2, id(lst2), id(lst2[2])) # [1, 2, [999, 4]] 2560120350016 2560120332096
print(lst3, id(lst3), id(lst3[2])) # [1, 2, [999, 4]] 2560120486784 2560120332096
在上面的例子中,lst1
的嵌套列表 [3, 4]
被浅拷贝到了 lst2
和 lst3
。因此,当修改 lst1
的嵌套列表时,lst2
和 lst3
中的相应部分也会受到影响,因为它们都引用了同一个嵌套列表。
对于字典来说,dict.copy()
方法同样执行浅拷贝。如果字典中包含的是可变对象(例如列表),那么这些可变对象不会被复制,而是被引用。
import copy
# 定义一个包含嵌套列表的字典
dict1 = {'a': 1, 'b': 2, 'c': [3, 4]}
# 使用字典的copy()方法进行浅拷贝
dict2 = dict1.copy()
# 使用copy模块的copy.copy()方法进行浅拷贝
dict3 = copy.copy(dict1)
# 修改原始字典中的嵌套列表
dict1['c'][0] = 999
# 打印结果,可以看到dict2和dict3的嵌套列表也被修改了,他们三个变量的内存地址都不一样,但是c键值对的内存地址都是一样的
print(dict1, id(dict1), id(dict1['c'])) # {'a': 1, 'b': 2, 'c': [999, 4]} 2128918277632 2128919277376
print(dict2, id(dict2), id(dict2['c'])) # {'a': 1, 'b': 2, 'c': [999, 4]} 2128918277696 2128919277376
print(dict3, id(dict3), id(dict3['c'])) # {'a': 1, 'b': 2, 'c': [999, 4]} 2128919274816 2128919277376
深拷贝
深拷贝(Deep Copy)是一种复制操作,它会创建一个新的复合对象,然后递归地复制原始对象中包含的所有子对象。这意味着深拷贝不仅复制了原始对象本身,还复制了原始对象内部所有的子对象,每个子对象也都得到了一个全新的副本。这样,原始对象及其所有子对象与复制的对象及其所有子对象之间没有任何共享的引用。每个变量包括嵌套都是不同的内存地址
在 Python 中,可以使用 copy
模块的 deepcopy()
函数来实现深拷贝。
import copy
# 定义一个包含嵌套列表的字典
original_dict = {'key1': [1, 2, 3], 'key2': {'inner_key': 4}}
# 使用deepcopy()进行深拷贝
copied_dict = copy.deepcopy(original_dict)
# 修改原始字典中的嵌套列表
original_dict['key1'][0] = 'a'
original_dict['key2']['inner_key'] = 'b'
# 打印结果,可以看到深拷贝的字典没有受到影响
print(original_dict) # {'key1': ['a', 2, 3], 'key2': {'inner_key': 'b'}}
print(copied_dict) # {'key1': [1, 2, 3], 'key2': {'inner_key': 4}}
评论区