本文和大家分享的主要是python
對(duì)象的可變類(lèi)型與不可變類(lèi)型相關(guān)內(nèi)容,一起來(lái)看看吧,希望對(duì)大家
學(xué)習(xí)python有所幫助。
python
對(duì)象對(duì)于修改這個(gè)行為,大家的操作方式都是不一樣的。有些對(duì)象是可變類(lèi)型,所以他就可以直接修改。其它的都是不可變類(lèi)型,那我們要修改他時(shí)怎么辦呢,這個(gè)時(shí)候就新生成的一個(gè)對(duì)象,然后將變量名指向這個(gè)新對(duì)象,修改動(dòng)作就完成了。關(guān)于可變類(lèi)型與不可變類(lèi)型的一個(gè)特點(diǎn),我們可以先看看一段奇怪的代碼
In [1]: a = 'a'
In [2]: b = 'a'
In [3]: a
is b
Out[3]: True
In [4]: a == b
Out[4]: True
In [5]: c = [1]
In [6]: d = [1]
In [7]: c
is d
Out[7]: False
In [8]: c == d
Out[8]: True
你有沒(méi)有覺(jué)得,a b
明明都是不同的對(duì)象,但是使用
a is b
的時(shí)候 竟然返回了
true
,這里面就會(huì)深入到對(duì)象的別名,標(biāo)識(shí)與相等性,先說(shuō)回到可變類(lèi)型與不可變類(lèi)型
分類(lèi)
不可變類(lèi)型
· int
· float
· decimal
· complex
· bool
· string
· tuple
· range
· frozenset
· bytes
可變類(lèi)型
· list
· dict
· set
· bytearray
· user-defined classes (unless specifically made immutable)
有個(gè)簡(jiǎn)單的方式來(lái)區(qū)分這些類(lèi)型,類(lèi)似的容器類(lèi)型的數(shù)據(jù)就是可變類(lèi)型(list,set,dict)—
我們就想像一個(gè)容器可以不停的注入內(nèi)容,其它的基本都是不可變的類(lèi)型,這里有幾個(gè)特殊的
·
元組
tuple
· frozenset
· str
區(qū)分類(lèi)型?有這個(gè)必要嗎?
當(dāng)然是顯然的,雖然我們?cè)趯?shí)際的工程環(huán)節(jié)都會(huì)圍繞整體的業(yè)務(wù)問(wèn)題,很少去了解底層的原理,但是我們一旦涉及到優(yōu)化,涉及到底層的時(shí)候就很有必要了解技術(shù)細(xì)節(jié)。舉個(gè)例子,如何高效的拼接字符串,我想下面的代碼大部分人都寫(xiě)過(guò),包括我
In [13]: str_build = ''
In [14]:
for p
in 'aaabbbccc':
...: str_build = str_build + p
事實(shí)上,這樣的效率并不是很高。如上所言,我們知道str
是不可變類(lèi)型,我們?cè)谄唇幼址臅r(shí)候,實(shí)際上是生成了一個(gè)新的對(duì)象,然后在變量名指向他,隨著拼接的次數(shù)越來(lái)越多,我們生成對(duì)象的操作次數(shù)也會(huì)越來(lái)越多,這樣就很沒(méi)有這個(gè)必要
(C#
里面的
StringBuilder
就是為了解決字符串拼接的
)
,那有效率的寫(xiě)法是怎樣的呢
builder_list = []
for data in container:
builder_list.
append(str(data))"".
join(builder_list)
### Another way
is
to use
a
list comprehension"".
join([str(data)
for data in container])
### or use the
map
function"".
join(
map(str, container))
我們通過(guò)使用列表,可以輕松的在本身對(duì)象的基礎(chǔ)上修改內(nèi)容,不會(huì)生成新對(duì)象來(lái)處理操作,這樣就會(huì)節(jié)省內(nèi)存
另外的一個(gè)我們會(huì)遇到關(guān)于可變類(lèi)型的一個(gè)坑
def
my_function(param=[]):
param.append("thing")
return param
my_function() # returns ["thing"]
my_function() # returns ["thing", "thing"]
你會(huì)不會(huì)覺(jué)得,我去,不管我調(diào)用多少次,這丫不是應(yīng)該就返回一個(gè)[‘thing’]
回來(lái)嗎?但是事實(shí)上你也看到了,因?yàn)榭勺儗?duì)象一直都會(huì)引用同一段內(nèi)容,我們?cè)诓僮髁斜淼臅r(shí)候都會(huì)對(duì)同一個(gè)列表操作,比如我們?cè)诤瘮?shù)體里面操作了一個(gè)
set ,list ,
或者
dict
,雖然我們沒(méi)有返回內(nèi)容,但是外部的內(nèi)容還是會(huì)變的,所以我們要記得,不要給可變對(duì)象做為參數(shù)的時(shí)候加上默認(rèn)值!
def
my_function2(param=None):
if param
is
None:
param = []
param.append("thing")
return param
結(jié)論
正確的區(qū)分可變類(lèi)型與不可變類(lèi)型,對(duì)于我們深入了解python
是非常有
python
的,更多的關(guān)注技術(shù)細(xì)節(jié),才能更好的拔高我們的技術(shù)水平
番外
我們?cè)谇把岳锩嬗刑岬降哪莻€(gè)奇怪的問(wèn)題,很顯然,那是共享了字符串的字面量,這種Cpython
里面一個(gè)優(yōu)化策略,叫駐留
(interning).CPython
還會(huì)在小的整數(shù)上使用這個(gè)優(yōu)化措施,防止重復(fù)的創(chuàng)建
”
熱門(mén)
“
數(shù)字,比如
0
,
-1
,和
42
等等,但是
CPython
不會(huì)駐留所有的字符串和整數(shù)。比如如下的代碼
In [5]: c = 1
In [6]: d = 1
In [7]: id(c)
Out[7]: 4297514912
In [8]: id(d)
Out[8]: 4297514912
In [9]: e = 22569
In [10]: f = 22569
In [11]: e
is f
Out[11]: False
In [12]: c
is d
Out[12]: True
所以千萬(wàn)不要依賴字符串或者整數(shù)的駐留!比較字符串或者整數(shù)是否相等的時(shí),應(yīng)該使用==
而不是
is.
駐留是
python
解釋器內(nèi)部使用的一個(gè)特性。
來(lái)源:大魚(yú)的博客