在初学Python时,经常需要区分“可变对象”和“不可变对象”。其中,可变对象包括列表(list)、字典(dict)等;不可变对象包括整型(int)、浮点型(float)、字符串型(string)和元组(tuple)等。
在进行可变对象与不可变对象的介绍之前,首先分析Python中对象的存储方式。每个对象都有标识、类型和值。对象一旦创建,将生成一个唯一不变的标识,可以把标识理解为对象在内存中的内存地址(ID)。
Python学习交流群:1004391443,这里是python学习者聚集地,有大牛答疑,有资源共享!小编也准备了一份python学习资料,有想学习python编程的,或是转行,或是大学生,还有工作中想提升自己能力的,正在学习的小伙伴欢迎加入学习。
关于“可变对象”和“不可变对象”的解释较多,为便于理解,我们进行如下定义:
① 可变对象:在ID不变的情况下,可以改变对象的值。
② 不可变对象:对象的值一旦改变,其ID也改变。
在Python中,参数传递模式为共享传参(call by sharing),即函数的各个形式参数获得实参中各个引用的副本。这种方案的结果是,函数可能会修改作为参数传入的可变对象,但无法修改那些对象的标识(即不能把一个对象替换成另一个对象)。
因此,对于当传递的是可变对象时,进行+=或*=等增量赋值是作用于对象本身,就地修改变量值。当传入的是不可变类型时,由于无法真正修改,往往创建新的对象,并赋予其新值。若变量是原对象的最后一个引用,原对象将被当作垃圾回收。
接下来,我们分别以列表(list)和元组(tuple)为例,直观呈现可变对象与不可变对象的区别。
(1)列表的可变性
<pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">lst = [1,3,5,7,9]print('ID:',id(lst))lst.append([11,13,15]) #在列表尾部增加一个新的子列表print('lst =',lst)print('new_ID:',id(lst)) #输出新的内存地址可见,将子列表[11,13,15]合并到原列表lst中后,列表值发生变化,但其内存地址ID保持不变。这是由于列表是可变对象,允许在其指向的内存空间就地修改元素,而不生成一个新的对象。
</pre>
<input class="pgc-img-caption-ipt" placeholder="图片描述(最多50字)" value="" style="box-sizing: border-box; outline: 0px; color: rgb(102, 102, 102); position: absolute; left: 187.5px; transform: translateX(-50%); padding: 6px 7px; max-width: 100%; width: 375px; text-align: center; cursor: text; font-size: 12px; line-height: 1.5; background-color: rgb(255, 255, 255); background-image: none; border: 0px solid rgb(217, 217, 217); border-radius: 4px; transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) 0s;"></tt-image>
(2)元组的不可变性
<pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">t = (1,3,5,7,9,11)print('ID:',id(t)) t += (15,17) #往元组内加入新的元素print('t =',t)print('new_ID:',id(t))
</pre>
<input class="pgc-img-caption-ipt" placeholder="图片描述(最多50字)" value="" style="box-sizing: border-box; outline: 0px; color: rgb(102, 102, 102); position: absolute; left: 187.5px; transform: translateX(-50%); padding: 6px 7px; max-width: 100%; width: 375px; text-align: center; cursor: text; font-size: 12px; line-height: 1.5; background-color: rgb(255, 255, 255); background-image: none; border: 0px solid rgb(217, 217, 217); border-radius: 4px; transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) 0s;"></tt-image>
可见,将元素15、17加入元组t后,t的ID在前后发生变化。这是由于元组本身不可变,在进行“+=”运算符操作时,将创建一个新元组,然后重新绑定给对象t,相当于执行“t=t+(15,17)”,前后并不是同一个对象。
需注意的是,这里的可变性与不可变性是针对具体对象而言。以元组为例,其更确切的定义应是相对不可变性。与多数Python集合(列表、字典、集,等等)一样,元组保存的是对象的引用。如果引用的元素是可变的,即便元组本身不可变,元素依然可变。也就是说,元组的不可变性实质上是指tuple数据结构的物理内容(即保存的引用)不可变,与引用的对象无关。
如下,元组tup的最后一个元素t[-1]引用的是可变列表对象,我们可以直接改变这一元素的值,并令整个元组tup的值发生变化。但由于该可变列表元素的标识(ID)不变,元组的ID也不改变,相当于就地修改了元组中特定元素的值。
<pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">tup = (1,3,5,[7,9,11])print('元组tup的ID:%d; 元素tup[-1]的ID:%d' %(id(tup),id(tup[-1])))tup[-1][1]=10print('tup =',tup)print('元组tup的new_ID:%d; 元素tup[-1]的new_ID:%d' %(id(tup),id(tup[-1])))
</pre>
<input class="pgc-img-caption-ipt" placeholder="图片描述(最多50字)" value="" style="box-sizing: border-box; outline: 0px; color: rgb(102, 102, 102); position: absolute; left: 187.5px; transform: translateX(-50%); padding: 6px 7px; max-width: 100%; width: 375px; text-align: center; cursor: text; font-size: 12px; line-height: 1.5; background-color: rgb(255, 255, 255); background-image: none; border: 0px solid rgb(217, 217, 217); border-radius: 4px; transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) 0s;"></tt-image>
我们进一步尝试修改元组本身的数据结构,在其后面增加新的元素。此时,元组tup的ID发送变化,表示内存中新生成了一个对象,其值为“tup=tup+(15,17)”。若没有其他变量名或对象引用的话,原对象占用的内存空间将被回收。
<pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">tup+= (15,17)print('tup=',tup)print('元组tup的new_ID:%d; 元素tup[-3]的new_ID:%d' %(id(tup),id(tup[-3])))
</pre>
<input class="pgc-img-caption-ipt" placeholder="图片描述(最多50字)" value="" style="box-sizing: border-box; outline: 0px; color: rgb(102, 102, 102); position: absolute; left: 187.5px; transform: translateX(-50%); padding: 6px 7px; max-width: 100%; width: 375px; text-align: center; cursor: text; font-size: 12px; line-height: 1.5; background-color: rgb(255, 255, 255); background-image: none; border: 0px solid rgb(217, 217, 217); border-radius: 4px; transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) 0s;"></tt-image>
综上,若不可变集合保存了可变元素的引用,当可变元素的值发生变化后,不可变集合也会随之改变,但其所含对象的标识不变。
网友评论