当前位置:首页>python>Python3里可变不可变数据类型,真的那么难搞吗 弄明白才不踩坑

Python3里可变不可变数据类型,真的那么难搞吗 弄明白才不踩坑

  • 2026-09-08 19:19:08
Python3里可变不可变数据类型,真的那么难搞吗 弄明白才不踩坑
很多人一开始就搞混了这个点

刚学Python的时候,谁没踩过这个坑啊。我之前遇到过一个学弟,写了半天函数,传进去参数改了半天,外面的变量一点没变,debug了俩小时都没找到问题。

最后一问,才知道他根本没搞懂可变和不可变数据类型到底是啥意思。

说起来这个概念,真的是新手劝退点,看着好像很简单,实际写代码分分钟给你脸色看。

你说记个列表可变,字符串不可变不就完了?真用到函数传参、赋值的时候,一堆奇怪的错误就冒出来了。

到底啥叫可变啥叫不可变

其实核心说穿了也简单,可变不可变,说的不是值能不能改,是内存地址变不变。

这个才是最容易搞错的地方啊。很多人刚学的时候,会误以为可变就是能改内容,不可变就是不能改内容,根本不是这么回事。

我记得之前看到过一个说法,说Python里所有变量其实就是个标签,贴在一块内存上,你改的时候,可变类型是直接改那块内存里的内容,标签不用挪地方;不可变类型呢,你想改?直接给你新开一块内存放新内容,标签挪到新地方,原来那块该咋样还是咋样。

举个例子你就懂了。你定义一个字符串a = "abc",然后你想把第一个字母改成大写,你试试能不能直接改?根本不行对吧,你只能重新赋值a = "Abc",这时候原来那块存"abc"的内存还在,只是a这个标签贴去新地址了,这就是不可变。

那列表呢?你定义b = [1,2,3],你直接把第一个元素改成10,b[0] = 10,你看看b的内存地址变了吗?没变,还是原来那块地方,直接改了里面内容,这就是可变。

就这么点事儿,绕清楚比啥都强。

常见类型给你理清楚

哪些是不可变类型?其实就那几个。数字、字符串、元组、布尔,这几个都是不可变的。

你别不服,试个简单的。

num = 10 print(id(num)) num = 20 print(id(num)) 

你打出来两个id,绝对不一样,对不对?因为改了值之后,直接换了内存地址,所以说数字是不可变。

元组这个坑,好多人掉进去过,说元组里面放个列表,我改列表不就改了元组内容了吗?那元组不就变成可变了?

不对不对,你好好想想,元组存的是什么?存的是每个元素的内存地址啊!元组本身不能改元素,你改的是那个列表里面的内容,元组里存的那个列表的地址根本没变啊!所以元组还是不可变的,这个弯一定要绕过来。

那可变类型呢?列表、字典、集合,这三个是典型的可变类型。

你自己试一下,改列表里面的元素,打出来的id还是原来的,绝对没错。

字典现在哪怕你用可变类型当键?不对啊,字典键要求必须不可变,因为要根据哈希值来找位置,可变类型哈希值会变,所以不能当键,这不就是从可变不可变衍生出来的规则嘛,根源就在这儿。

写代码最容易踩的坑就是这个

说一万遍不如实际踩一次,最常见的坑就是函数传默认参数的时候。

好多人刚学写函数,默认参数写个列表,结果下次调用的时候,上次加进去的内容居然还在!吓不吓人?

我之前写过这么一个代码,坑了我好半天:

def add_item(item, my_list = []):     my_list.append(item)     return my_list 

你第一次调用add_item(1),返回[1],第二次调用add_item(2),你以为会返回[2]吗?错了,返回的是[1,2]!

为啥啊,你想想就懂了,默认参数是在定义函数的时候就创建好的,list是可变类型啊,你每次调用,改的都是同一个内存地址里的内容,当然会保留上次的结果啊!

这就是典型的因为没搞懂可变不可变踩的坑,我当时刚学的时候,看到结果整个人都懵了,以为Python出bug了。

后来搞懂了之后,才发现这个坑就是专门给新手准备的,好多人学了半天概念,一到实际用就错。

还有一个坑就是赋值的时候,比如a = [1,2,3],然后b = a,你改b[0] = 10,你回头看看a,居然也变了!为啥啊,因为a和b指向的是同一个内存地址啊,列表是可变类型,你改b就是改那块内存里的内容,a当然也跟着变了。

如果是不可变类型呢?比如a = 10,b = a,然后b = 20,这时候a还是10啊,因为b赋值20的时候,直接换了个内存地址,跟原来a没关系了。

就这一点,多少人搞混了啊。

那我想复制一个可变类型怎么办?

你总不能每次都看着两个变量一起变啊,想要复制怎么办?浅拷贝深拷贝这不就来了吗?根源还是在可变不可变啊。

我有个朋友做数据分析,上次写代码,改完复制出来的新列表,结果原数据跟着变,整个分析结果全错了,白做了一下午,说起来都是泪。

如果你直接b = a,那就是两个变量指向同一个对象,改了肯定一起变啊。想要真正复制一个新的列表,得用拷贝啊。

最简单的浅拷贝,列表的话用b = a.copy(),或者切片b = a[:],这时候新列表b有自己的内存地址了,改b的第一层元素,a不会变。

要是列表里面还套了列表呢?那浅拷贝也不行,得用深拷贝,import copy然后b = copy.deepcopy(a),这时候所有层都是新的,怎么改都不影响原来的。

你说这一套下来,核心是不是还是可变不可变那点事儿?要是一开始就搞懂,哪会错成这样啊。

为啥Python要搞这么一出

说真的,我一开始也觉得,为啥要分这个,多麻烦啊,直接都能改不就完了。后来才想明白,不可变类型有好处啊。

不可变类型,你存东西的时候,相同的值可以共享内存啊,省空间啊,比如你有好几个变量都等于1,那其实它们都指向同一个内存里的1就行了,不用每个都新开一块,多省地方。

而且不可变天生就是线程安全的啊,大家都只能读不能改,哪来的修改冲突啊,多省心。

可变类型呢,就是灵活啊,你想改就改,不用每次都新开一块,效率高啊,存大列表的时候,总不能每次改一个元素都复制一遍整个列表吧,那得多慢啊。

各有各的用处,所以才分了可变不可变,不是Python闲得慌给你找事儿做。

我个人觉得,这个设计其实挺巧妙的,只是新手上来很难一下子体会到,上来先踩一堆坑,踩多了才慢慢明白,哦原来这么设计是有道理的啊。

最后再给你划个重点吧

其实说了这么多,最核心的一句话就是可变改内容不换地址,不可变改值直接换地址,把这句话刻进脑子里,写代码的时候多想想,90%的坑就能避开了。

别不信,真的,好多人学Python半年多了,遇到这个问题还是懵,写代码凭感觉,错了才回头找原因,要是一开始就把这个概念掰扯清楚,后面能少走好多弯路。

这个问题我也没说全,比如还有自定义类的实例,可变不可变其实还和你怎么定义有关,那部分我了解得也不够深入,就不多说了。

反正就记住常见的那几个类型,记住内存地址那个核心点,下次再遇到传参、赋值出奇怪问题,先往这儿想想,大概率就是这个问题搞的鬼。

最新文章

随机文章