为什么Python中的函数会修改全局的列表和字典_python 线程全局dict 会改变

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

需要这份系统化学习资料的朋友,可以戳这里获取

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

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开始计数,然后遍历字典中的每个值,将它们添加到计数中,这样就得到了评级的总数。然后我们将再次遍历字典,并对每个值做一些数学运算来计算百分比。

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() #adds something to a list
list.copy()# makes a copy of a list

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

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的全局值将保持不变。

不过,这个解决方案仍然不完美,因为每次向函数传递参数时都必须记得添加.copy(),否则可能会意外更改initial_list的全局值。如果我们不想操心这个,我们可以在函数内部创建列表拷贝:

initial_list =[1,2,3]
def duplicate_last(a_list):
    copy_list = a_list.copy()
    last_element = copy_list[-1]
    copy_list.append(last_element)
    return copy_list

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

输出:

[1, 2, 3, 3]


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

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。

![img](https://img-blog.csdnimg.cn/1d40facda2b84990b8e1743f5487d455.png)  
![img](https://img-blog.csdnimg.cn/0fc11d4a31bd431dbf124f67f1749046.png)

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

工具都帮大家整理好了,安装就可直接上手!![img](https://img-blog.csdnimg.cn/ff266f529c6a46c4bc28e5f895dec647.gif#pic_center)

**三、最新Python学习笔记**

当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。

![img](https://img-blog.csdnimg.cn/6d414e9f494742db8bcc3fa312200539.png)

**四、Python视频合集**

观看全面零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

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

**五、实战案例**

纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。![img](https://img-blog.csdnimg.cn/a353983317b14d3c8856824a0d6186c1.png)

**六、面试宝典**

![在这里插入图片描述](https://img-blog.csdnimg.cn/97c454a3e5b4439b8600b50011cc8fe4.png)

![在这里插入图片描述](https://img-blog.csdnimg.cn/111f5462e7df433b981dc2430bb9ad39.png)

###### **简历模板**![在这里插入图片描述](https://img-blog.csdnimg.cn/646863996ac44da8af500c049bb72fbd.png#pic_center)




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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值