为什么Python中的函数会修改全局的列表和字典_python 列表 传入def函数后会改变


正如我们在上面看到的,函数正常运行,全局变量number\_1和number\_2的值没有变化,尽管我们在函数中使用它们作为形参和实参名。这是因为Python将函数中的变量存储在与全局变量不同的内存位置。它们是被隔离的。因此,变量number\_1可以在全局中有一个值(5),而在函数内部有一个不同的值(50),在这个函数中它是独立的。


(顺便说一句,如果你对parameters(形参) 和 arguments(实参)之间的区别感到困惑,Python文档中关于这个主题的内容非常有用。)


#### **那么列表和字典呢?**


**列表**


我们已经看到,我们在函数内部对上面的number\_1这样的变量所做的操作并不影响它的全局值。但是number\_1是一个整数,这是一个非常基本的数据类型。如果我们用不同的数据类型(比如列表)尝试相同的实验,会发生什么?下面,我们将创建一个名为duplicate\_last() 的函数,它将复制我们作为参数传递的任何列表中的最终条目。



initial_list=[1,2,3]

def duplicate_last(a_list):
last_element = a_list[-1]
a_list.append(last_element)
return a_list

new_list = duplicate_last(a_list = initial_list)
print(new_list)
print(initial_list)

#输出
[1, 2, 3, 3]
[1, 2, 3, 3]


正如我们所看到的,这里 initial\_list 的全局值被更新了,即使它的值只在函数内部更改!


**字典**


现在,我们来编写一个函数,该函数以一个字典作为参数来查看全局字典变量在函数中被操作时是否也会被修改。


为了看起来更直观一点,我们将使用Python基础课程中使用的 AppleStore.csv 数据集中的数据(数据可以从这里下载)。


在下面的代码片段中,我们从一个字典开始,它包含了数据集中各个年龄级别的应用程序的数量(因此有4433个应用程序的级别为“4+”,987个应用程序的级别为“9+”,等等)。假设我们想计算每个年龄等级的百分比,这样我们就可以得到在App Store中哪个年龄等级是最常见的。


为此,我们将编写一个名为 make\_percentages() 的函数,该函数以一个字典作为参数并将计数转换为百分比。我们需要从0开始计数,然后遍历字典中的每个值,将它们添加到计数中,这样就得到了评级的总数。然后我们将再次遍历字典,并对每个值做一些数学运算来计算百分比。



‘’’
学习中遇到问题没人解答?小编创建了一个Python学习交流QQ群:725638078
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
‘’’
content_ratings = {‘4+’:4433,‘9+’:987,‘12+’:1155,‘17+’:622}

def make_percentages(a_dictionary):
total =0
for key in a_dictionary:
count = a_dictionary[key]
total += count
for key in a_dictionary:
a_dictionary[key]= (a_dictionary[key] / total)*100

return a_dictionary

在查看输出之前,让我们快速回顾一下上面发生的事情。在将我们的app 年龄评级字典分配给变量content\_ratings之后,我们创建了一个名为make\_percentages()的新函数,它只接受一个参数: a\_dictionary。


为了计算每个年龄等级的应用程序所占比例,我们需要知道应用程序的总数,因此我们首先将一个名为total的新变量设置为0,然后在a\_dictionary中循环遍历每个键值,并将其添加到total中。


完成之后,我们需要做的就是再次遍历a\_dictionary,将每个条目除以总数,然后将结果乘以100。这将给我们返回一个包含百分比的词典。


但是,当我们使用全局content\_ratings变量作为这个新函数的参数时发生了什么呢?



c_ratings_percentages = make_percentages( content_ratings)
print(c_ratings_percentages)
print(content_ratings)
#输出
{‘4+’: 61.595109073224954, ‘9+’: 13.714047519799916, ‘12+’: 16.04835348061692, ‘17+’: 8.642489926358204}
{‘4+’: 61.595109073224954, ‘9+’: 13.714047519799916, ‘12+’: 16.04835348061692, ‘17+’: 8.642489926358204}


正如我们在列表中看到的,我们的全局content\_ratings变量已经更改,尽管它只是在我们创建的make\_percentages()函数中进行了修改。


这里到底发生了什么?我们遇到了**可变**和**不可变**数据类型之间的差异。


**可变和不可变的数据类型**


在Python中,数据类型可以是可变的(可更改的),也可以是不可变的(不可更改的)。虽然我们在介绍Python时使用的大多数数据类型都是不可变的(包括整数、浮点数、字符串、布尔值和元组),但是列表和字典是可变的。这意味着**全局列表或字典即使在函数内部使用时也可以更改**,就像我们在上面的示例中看到的那样。


要理解可变(可更改)和不可变(不可更改)之间的区别,了解Python如何处理这些变量是很有帮助的。


让我们从考虑一个简单的变量赋值开始:



a = 5


变量名a的作用类似于一个指向5的指针,它可以帮助我们随时检索5。


5是一个整数,整数是不可变的数据类型。如果数据类型是不可变的,这意味着一旦创建,就不能更新它。如果我们执行a += 1,我们实际上并没有更新5到6。在下面的动画中,我们可以看到这一点:


a 初始指向 5.


执行a += 1 后, 将指针从 5指向 6, 它并没有实际改变 5.


可变数据类型(如列表和字典)的行为有所不同。它们可以更新。举个例子,我们来创建一个非常简单的列表:



list_1 = [1,2]


如果我们在列表末尾添加一个3,我们不是简单地将list\_1指向另一个列表,而是直接更新现有列表:


即使我们创建多个列表变量,只要它们指向同一个列表,当列表被更改时,它们都会被更新,如下面的代码所示:



list_1 = [1,2]
list_2 = list_1
list_1.append(3)
print(list_1)
print(list_2)
#输出
[1, 2, 3]
[1, 2, 3]


**下面是上面代码中实际发生的动态可视化:**


这就解释了为什么我们之前在试验列表和字典时我们的全局变量被改变了。因为列表和字典是可变的,所以更改它们(即使是在函数中)也会更改列表或字典本身,这与不可变数据类型不同。


**保持可变数据类型不变**


一般来说,我们不希望函数更改全局变量,即使它们包含列表或字典之类的可变数据类型。这是因为在更复杂的分析和程序中,我们可能会经常使用许多不同的函数。如果所有函数都更改它们正在调用的列表和字典,那么要跟踪什么在更改什么就会变得非常困难。


幸运的是,有一种简单的方法可以绕过这个问题:我们可以使用内建的Python方法.copy()复制列表或字典。


如果你还没有学习过方法,请不要担心。它们包含在我们的中级Python课程中,但是在本教程中,你只需要知道.copy()的工作原理类似于.append():



list.append()
list.copy()


让我们再看一下我们为列表写的函数,对它进行更新,这样函数内部执行的操作就不会更改initial\_list。我们只需要将传递给函数的参数从initial\_list更改为initial\_list.copy()



‘’’
学习中遇到问题没人解答?小编创建了一个Python学习交流QQ群:725638078
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
‘’’
initial_list=[1,2,3]
def duplicate_last(a_list):
last_element = a_list[-1]
a_list.append(last_element)
return a_list
new_list = duplicate_last(a_list = initial_list.copy())
print(new_list)
print(initial_list)
#输出
[1, 2, 3, 3]
[1, 2, 3]


正如我们所看到的,这已经解决了我们的问题。原因如下:使用.copy()创建一个列表的独立副本,这样a\_list就不会指向initial\_list本身,而是指向一个以initial\_list副本开始的新列表。在此之后对a\_list所做的任何更改都将只对该独立列表生效,而不是initial\_list本身,因此initial\_list的全局值将保持不变。

文末有福利领取哦~
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

👉**一、Python所有方向的学习路线**

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。![img](https://img-blog.csdnimg.cn/c67c0f87cf9343879a1278dfb067f802.png)

👉**二、Python必备开发工具**

![img](https://img-blog.csdnimg.cn/757ca3f717df4825b7d90a11cad93bc7.png)  
👉**三、Python视频合集**

观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。  
![img](https://img-blog.csdnimg.cn/31066dd7f1d245159f21623d9efafa68.png)

👉 **四、实战案例**

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。**(文末领读者福利)**  
![img](https://img-blog.csdnimg.cn/e78afb3dcb8e4da3bae5b6ffb9c07ec7.png)

👉**五、Python练习题**

检查学习结果。  
![img](https://img-blog.csdnimg.cn/280da06969e54cf180f4904270636b8e.png)

👉**六、面试资料**

我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。  
![img](https://img-blog.csdnimg.cn/a9d7c35e6919437a988883d84dcc5e58.png)

![img](https://img-blog.csdnimg.cn/5db8141418d544d3a8e9da4805b1a3f9.png)

👉因篇幅有限,仅展示部分资料,这份完整版的Python全套学习资料已经上传




**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化学习资料的朋友,可以戳这里无偿获取](https://bbs.csdn.net/topics/618317507)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值