在咱们编程当中,变量是最最根底的概念,它的重要就相当于咱们盖大楼用的一块砖一样,是不可或缺的。所以,了解变量的运行形式是至关重要的。
九层之台,始于垒土;合抱之木,始于毫末;千里之行,始于足下!
明天就让咱们一起来谈一谈 Python 变量的那些事。
1. 变量不是盒子
让咱们看看上面的代码
1. a = "hello,world"
2. b = a
3. c = [1,2,3]
对于咱们初学者来说,变量的赋值是最容易走进误区的中央。最常见的误区是什么呢?
定义一个变量,就在内存中创立一个变量盒子,而后把变量的值放在这个盒子中
让咱们看看上面这张图,这种想法是大错特错的。就是因为这种误区,使得咱们的代码可能遇到很多问题。
那正确的是什么?变量赋值的时候做了什么呢?
2. 千奇百怪的变量
1. a = "hello,world
2. b = a
3. c = [1,2,3]
所以下面的代码到底做了什么?在这之前,我先大家讲个故事。
上个世纪 90 年代的时候,咱们赫赫有名的蟒蛇自助大酒楼停业了。顾名思义,这家酒楼次要是做自助餐的,然而呢?它每个顾客只能吃一种美食,并且会把美食散布在不同的房间外面,美食品种是不固定的,顾客有什么需要就提供什么。
这天,咱们的小 a 同学来到了酒楼,跟前台说,我要吃“hello,world”,于是,酒店前台就新开了一间房间,房号为:00010,并且在外面放上了咱们的“hello,world”,并且给了 a 一张通行证,这张通行证只能通往 00010 号房间。并且记录下 hello,world:食用人数:1,当 a 想吃的时候,就本人拿通行证去 00010 号房间去拿
过不久,小 b 也来到了咱们的酒店,跟酒店前台说,我要跟 a 吃一样的货色。于是酒店前台,也给了 b 一张通行证,b 依据通行证也能去到 00010 号房间去拿 hello,world。酒店前台再次记录:hello,world:食用人数:2
紧接着,咱们小 c 同学也来了,他跟 b 不一样,他有本人想吃的食物。他跟酒店前台说:我要吃[1,2,3]。顾客有了新的需要,酒店前台就又新开一件房间,房号为:00020,并且也在外面放上了[1,2,3]。同样给 c 一张通行证。而后记录下:[1,2,3]:食用人数:1
咱们能够带着这个故事往下面看
依据这张图,咱们下面的故事中:
顾客 a、b、c:变量 a、b、c\
酒店:内存空间
酒店前台:Python 解释器
房间:为对象划分的内存空间
房间号:对象所在的内存地址
食物:各种各样的对象(字符串、列表、字典、数字。。。)
前台记录的食用人数:援用计数
通信证号码:变量援用的内存地址
实际上当咱们对一个变量赋值的时候,咱们的变量并没有存储这个值。而是绑定了一个内存地址 id,当咱们要用这个变量的值的时候,就去内存中寻找这个地址的存储的值
接着下面的故事,咱们的小 a 同学,吃腻了 hello,world,当初想吃 123456,于是跑去跟酒店前台说,我当初想吃 123456 了,酒店前台二话不说,新开了一间房间,房号为 00030,外面放着 123456,并且更新了 a 同学的通行证,此时这张通信证只能去 00030 号房吃 123456。前台持续记录 hello,world:食用人数:1、123456:食用人数:1
在代码中,咱们扭转了 a 变量的值,会产生什么呢?
咱们再看看,扭转 a 的变量会产生什么?
a = 123456
会这样吗?
咱们扭转 a 的值的时候,并不会间接去扭转 a 指向的内存地址存储的值,而是新开拓一个空间寄存新的值 123456,把 a 的指向改成新空间的地址 00030,如下图所示。正确的应该是这样:
咱们的 b 同学十分喜爱模拟,她当初又不想吃 hello,world 了。于是他就跑下楼去跟酒店前台说:我要吃[1,2,3]。留神哦,这次 b 同学说的是,我要吃[1,2,3],而不是说我要吃跟 c 一样的。尽管他们的食物是一样的。然而咱们的前台并不会间接给 b 00020 号房间的通行证。而是新开一间房间,房间号 00040,外面放[1,2,3],并且给 b 一张通行证指向 00040 号房间。共事记录上(00040)[1,2,3]:食用人数:1、(00010)hello,world:食用人数:0
b = [1,2,3]
为什么呢?其实这里很好了解,因为咱们 b 赋值的时候是新建了一个对象。只有新建对象,就会从新开拓空间。
然而,像这样
b = c
这样并没有新建对象,而是将 c 的援用传递给了 b,他们都指向一个对象。这里小伙伴们注意一下,不要被我的例子给带跑偏了。
这个时候,咱们的酒店前台发现 00010 号房间外面的 hello,world 曾经没人吃了。这个时候酒店前台就会把这间房间发出来,并且把外面的 hello,world 食物丢掉。这个就是 python 的垃圾回收机制。
此时,又来了认为同学 d,d 跟酒店前台说,我要跟 c 吃一样的,酒店前台就给 d 发了一张通行证,d 依据通行证能去到 00020 号房间去拿[1,2,3]。酒店前台再次记录:[1,2,3]:食用人数:2
d = c
然而咱们的 d 同学十分挑剔,他不满足现有的[1,2,3],他想要加点菜,也是就跟前台说要加菜:这时候 00020 房间外面放的货色就变成了[1,2,3,4]
d.append(4)
此时,咱们发现了一个问题,c 同学什么也没有干,然而他能吃的食物从 [1,2,3] 变成了[1,2,3,4]
这到底是什么问题呢?
为什么咱们之前 a 从“hello,world”变成 123456 的时候,是新开拓一块空间。然而当初 d 从 [1,2,3] 变成[1,2,3,4],却间接在原内存空间里批改呢?
这就是 python 经典的面试题:对象的可变性?什么是可变对象,什么是不可变对象?
3. 可变对象与不可变对象
在 python 中,所有皆对象,然而这对象也分为两类:
可变对象(3 个):List(列表)、Dictionary(字典)、Set(汇合)
不可变对象(3 个):Number(数字)、String(字符串)、Tuple(元组)
Python 中看可变与不可变数据类型,次要是看变量所指向的内存地址处的值是否会扭转。
3.2 不可变对象
>>> a = 10000
>>> id(a)
139964684838128
>>> a = 30000 # 不可变对象,扭转变量的值,实际上是实例化新对象、开拓新内存空间
>>> id(a) # 产生了新的内存地址,阐明曾经不是原来的对象了 139964684837872
>>>
3.3 可变对象
>>> a = [1,2,3]
>>> b = a
>>> id(a)
139711046464264
>>> id(b)
139711046464264
>>> b.append(4) # 可变对象,容许在原地扭转对象的值
>>> id(b)
139711046464264 # 内存地址没有扭转,阐明是在原内存空间扭转值
>>> id(a)
139711046464264
>>> b
[1, 2, 3, 4]
>>> a
[1, 2, 3, 4]
总结:
可变对象:变量所指向的内存地址处的值是能够被扭转的
不可变对象:变量所指向的内存地址处的值是不能够被扭转。