翼度科技»论坛 编程开发 python 查看内容

Python关于实参随形参改变而改变的问题

6

主题

6

帖子

18

积分

新手上路

Rank: 1

积分
18
前言

今天在实验过程中,发现将字典作为函数的形参传入函数,在函数内改变形参,会导致传入的字典的值也发生相应的改变。
这与
  1. c++
复制代码
不同,令我疑惑,遂写此文。

简单实验

我们对常见的数据类型进行实验,检测形参的该表是否会改变传入的实参。

变量
  1. def change(a):
  2.     a = 2
  3.     print(a)
  4. b = 1
  5. change(b)
  6. print(b)
  7. >>2
  8. >>1
复制代码
可见,变量的值并没有随形参的改变而改变

元组
  1. def change(a):
  2.     a = a[:2]
  3.     print(a)
  4. b = (1, 2, 3)
  5. change(b)
  6. print(b)
  7. >>(1, 2)
  8. >>(1, 2, 3)
复制代码
可见,元组没有随形参的改变而改变

列表
  1. def change(a):
  2.     a.append(1)
  3.     print(a)
  4. b = [2, 3]
  5. change(b)
  6. print(b)
  7. >>[2, 3, 1]
  8. >>[2, 3, 1]
复制代码
可见,列表随着形参的改变而发生改变

字典
  1. def change(a):
  2.     a[1] = 100
  3.     print(a)
  4. b = {1: 1, 2: 2}
  5. change(b)
  6. print(b)
  7. >>{1: 100, 2: 2}
  8. >>{1: 100, 2: 2}
复制代码
可见,字典随着形参的改变而发生改变

原因

我们遇到了可变不可变数据类型之间的差异。在
  1. Python
复制代码
中,数据类型可以是可变的,也可以是不可变的。
我们通常使用的(整数,浮点数,字符串,布尔值和元组)数据类型都是不可变的,但是列表和字典是可变的。
这意味着全局列表或字典即使在函数内部使用时也可以更改,我们通过上面的例子也能看出这个问题。

不可变数据举例
  1. [code]a <span>=</span> <span>1</span>
复制代码
[/code]变量
  1. a
复制代码
的作用类似于一个指向 1 的指针。
变量的数据类型是不可变的,变量一旦创建就不能别改变。
如果我们执行
  1. a = a + 1
复制代码

我们实际上不是将 1 更新到 2,而是指针从 1 指向了 2。

可变数据距离
  1. list1 = [1, 2, 3]
复制代码
如果我们在列表的末尾添加一个值,我们不是将
  1. list1
复制代码
指向另一个列表,而是直接更新现有列表。
如果我们创建多个列表变量,只要他们指向同一个列表,那么当列表发生改变时,这些列表变量都会发生变化。
  1. list1 = [1, 2, 3]
  2. list2 = list1
  3. list1.append(4)
  4. print(list1)
  5. print(list2)
  6. >>[1, 2, 3, 4]
  7. >>[1, 2, 3, 4]
复制代码
心得

从这两个例子中,我们可以直观感受到可变数据类型与不可变数据类型之间的区别。
我们对可变数据的操作,是直接在其本身上进行操作的。
对不可变数据的操作,是将指针指向另一个位置,而不是更改其本身。

保持可变数据不变

我们在写代码时,经常会编写各种函数。我们不希望传入函数的实参,会在函数内部被改变。那应该怎么办呢?
其实很简单,使用
  1. .copy()
复制代码
方法复制列表或字典即可。
  1. list1 = [1, 2, 3]
  2. list2 = list1.copy()
  3. list1.append(4)
  4. print(list1)
  5. print(list2)
  6. >>[1, 2, 3, 4]
  7. >>[1, 2, 3]
复制代码
  1. .copy()
复制代码
方法会创建一个新的副本,这样
  1. list2
复制代码
就不会指向
  1. list1
复制代码
指向的列表,而是指向一个新的列表。
这样的话,
  1. list1
复制代码
  1. list2
复制代码
就相互独立,互不影响了。
  1. def change(a):
  2.     temp = a.copy()
  3.     temp.append(4)
  4.     print(temp)
  5. list1 = [1, 2, 3]
  6. change(list1)
  7. print(list1)
  8. >>[1, 2, 3, 4]
  9. >>[1, 2, 3]
复制代码
可见,这样我们就解决了形参的改变带来的实参改变的问题。对于字典也是一样的。

补充


深拷贝与浅拷贝
  1. >>> import copy
  2. >>> origin = [1, 2, [3, 4]]
  3. #origin 里边有三个元素:1, 2,[3, 4]
  4. >>> cop1 = copy.copy(origin)
  5. >>> cop2 = copy.deepcopy(origin)
  6. >>> cop1 == cop2  # 判断 cop1 和 cop2 的值是否相同
  7. True
  8. >>> cop1 is cop2  # 判断 cop1 和 cop2 是否是同一个对象
  9. False
  10. #cop1 和 cop2 看上去相同,但已不再是同一个object
  11. >>> origin[2][0] = "hey!"
  12. >>> origin
  13. [1, 2, ['hey!', 4]]
  14. >>> cop1
  15. [1, 2, ['hey!', 4]]
  16. >>> cop2
  17. [1, 2, [3, 4]]
复制代码
copy对于一个复杂对象的子对象并不会完全复制。什么是复杂对象的子对象呢?
比如列表里的嵌套列表(多维列表),字典里的嵌套字典(多维字典)都是复杂对象的子对象。
对于子对象,
  1. python
复制代码
会将其当做一个公共镜像存储起来,所有对它的复制都会被当成引用(就是拿指针指向同一块区域)。所以,其中一个引用的值发生改变时,其他引用的值也会发生改变。
复制复杂对象,我们也想全盘复制(包括子对象),这时我们就可以使用
  1. deepcopy()
复制代码
函数进行深拷贝
  1. import copy
  2. a = [1, 2, [3, 4]]
  3. b = copy.deepcopy(a)  # 深拷贝
复制代码
总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

来源:https://www.jb51.net/python/3310087qq.htm
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具