Python基礎(chǔ)之變量的相關(guān)知識總結(jié)
變量全都是引用
跟其他編程語言不同,Python的變量不是盒子,不會存儲數(shù)據(jù),它們只是引用,就像標(biāo)簽一樣,貼在對象上面。
比如:
>>> a = [1, 2, 3] >>> b = a >>> a.append(4) >>> b [1, 2, 3, 4] >>> b is a True
a變量和b變量引用的是同一個列表[1, 2, 3]
。b可以叫做a的別名。
比較來看:
>>> a = [1, 2, 3] >>> c = [1, 2, 3] >>> c == a True >>> c is a False
c引用的是另外一個列表,雖然和a引用的列表的值相等,但是它們是不同的對象。
淺復(fù)制與深復(fù)制
淺復(fù)制是指只復(fù)制最外層容器,副本中的元素是源容器中元素的引用。如果所有元素都是不可變的,那么這樣沒有問題,還能節(jié)省內(nèi)容。但是,如果有可變的元素,那么結(jié)果可能會出乎意料之外。構(gòu)造方法或[:]
做的都是淺復(fù)制。
示例:
>>> x1 = [3, [66, 55, 44], (7, 8, 9)] # x2是x1的淺復(fù)制 >>> x2 = list(x1) # 不可變元素沒有影響 >>> x1.append(100) >>> x1 [3, [66, 55, 44], (7, 8, 9), 100] >>> x2 [3, [66, 55, 44], (7, 8, 9)] # x1[1]是列表,可變元素會影響x2 # 因?yàn)樗鼈円玫氖峭粋€對象 >>> x1[1].remove(55) >>> x1 [3, [66, 44], (7, 8, 9), 100] >>> x2 [3, [66, 44], (7, 8, 9)] # x2[1]也會反過來影響x1 >>> x2[1] += [33, 22] >>> x1 [3, [66, 44, 33, 22], (7, 8, 9), 100] >>> x2 [3, [66, 44, 33, 22], (7, 8, 9)] # 不可變元組也不會有影響 # +=運(yùn)算符創(chuàng)建了一個新元組 >>> x2[2] += (10, 11) >>> x1 [3, [66, 44, 33, 22], (7, 8, 9), 100] >>> x2 [3, [66, 44, 33, 22], (7, 8, 9, 10, 11)]
深復(fù)制是指我們常規(guī)理解的復(fù)制,副本不共享內(nèi)部對象的引用,是完全獨(dú)立的一個副本。這可以借助copy.deepcopy來實(shí)現(xiàn)。
示例:
>>> a = [10, 20] >>> b = [a, 30] >>> a.append(b) >>> a [10, 20, [[...], 30]] >>> from copy import deepcopy >>> c = deepcopy(a) >>> c [10, 20, [[...], 30]]
即使是有循環(huán)引用也能正確復(fù)制。
注意copy.copy()是淺復(fù)制,copy.deepcopy()是深復(fù)制。
函數(shù)傳參
Python唯一支持的參數(shù)傳遞模式是共享傳參,也就是指函數(shù)的各個形式參數(shù)獲得實(shí)參中各個引用的副本。因?yàn)镻ython的變量全都是引用。對于不可變對象來說沒有問題,但是對于可變對象就不一樣了。
示例:
>>> def f(a, b): ... a += b ... return a ... # 數(shù)字不變 >>> x = 1 >>> y = 2 >>> f(x, y) 3 >>> x, y (1, 2) # 列表變了 >>> a = [1, 2] >>> b = [3, 4] >>> f(a, b) [1, 2, 3, 4] >>> a, b ([1, 2, 3, 4], [3, 4]) # 元組不變 >>> t = (10, 20) >>> u = (30, 40) >>> f(t, u) (10, 20, 30, 40) >>> t, u ((10, 20), (30, 40))
由此可以得出一條警示:函數(shù)參數(shù)盡量不要使用可變參數(shù),如果非用不可,應(yīng)該考慮在函數(shù)內(nèi)部進(jìn)行復(fù)制。
示例:
class TwilightBus: """A bus model that makes passengers vanish""" def __init__(self, passengers=None): if passengers is None: self.passengers = [] else: self.passengers = passengers def pick(self, name): self.passengers.append(name) def drop(self, name): self.passengers.remove(name)
測試一下:
>>> basketball_team = ['Sue', 'Tina', 'Maya', 'Diana', 'Pat'] >>> bus = TwilightBus(basketball_team) >>> bus.drop('Tina') >>> bus.drop('Pat') >>> basketball_team ['Sue', 'Maya', 'Diana']
TwilightBus下車的學(xué)生,竟然從basketball_team中消失了。這是因?yàn)閟elf.passengers引用的是同一個列表對象。修改方法很簡單,復(fù)制個副本:
def __init__(self, passengers=None): if passengers is None: self.passengers = [] else: self.passengers = list(passengers) # 使用構(gòu)造函數(shù)復(fù)制副本
del和垃圾回收
del語句刪除的是引用,而不是對象。但是del可能會導(dǎo)致對象沒有引用,進(jìn)而被當(dāng)做垃圾回收。
示例:
>>> import weakref >>> s1 = {1, 2, 3} # s2和s1引用同一個對象 >>> s2 = s1 >>> def bye(): ... print("Gone") ... # 監(jiān)控對象和調(diào)用回調(diào) >>> ender = weakref.finalize(s1, bye) >>> ender.alive True # 刪除s1后還存在s2引用 >>> del s1 >>> ender.alive True # s2重新綁定導(dǎo)致{1, 2, 3}引用歸零 >>> s2 = "spam" Gone # 對象被銷毀了 >>> ender.alive False
在CPython中,對象的引用數(shù)量歸零后,對象會被立即銷毀。如果除了循環(huán)引用之外沒有其他引用,兩個對象都會被銷毀。
弱引用
某些情況下,可能需要保存對象的引用,但不留存對象本身。比如,有個類想要記錄所有實(shí)例。這個需求可以使用弱引用實(shí)現(xiàn)。
比如上面示例中的weakref.finalize(s1, bye),finalize就持有{1, 2, 3}
的弱引用,雖然有引用,但是不會影響對象被銷毀。
其他使用弱引用的方式是WeakDictionary、WeakValueDictionary、WeakSet。
示例:
class Cheese: def __init__(self, kind): self.kind = kind def __repr__(self): return 'Cheese(%r)' % self.kind >>> import weakref >>> stock = weakref.WeakValueDictionary() >>> catalog = [Cheese('Red Leicester'), Cheese('Tilsit'), ... Cheese('Brie'), Cheese('Parmesan')] ... >>> for cheese in catalog: # 用作緩存 # key是cheese.kind # value是cheese的弱引用 ... stock[cheese.kind] = cheese ... >>> sorted(stock.keys()) ['Brie', 'Parmesan', 'Red Leicester', 'Tilsit'] # 刪除catalog引用,stock弱引用不影響垃圾回收 # WeakValueDictionary的值引用的對象被銷毀后,對應(yīng)的鍵也會自動刪除 >>> del catalog >>> sorted(stock.keys()) # 還存在一個cheese臨時變量的引用 ['Parmesan'] # 刪除cheese臨時變量的引用,stock就完全清空了 >>> del cheese >>> sorted(stock.keys()) []
注意不是每個Python對象都可以作為弱引用的目標(biāo),比如基本的list和dict就不可以,但是它們的子類是可以的:
class MyList(list): pass a_list = MyList(range(10)) weakref_to_a_list = weakref.ref(a_list)
小結(jié)
本文首先闡述了Python變量全部都是引用的這個事實(shí),這意味著在Python中,簡單的賦值是不創(chuàng)建副本的。如果要創(chuàng)建副本,可以選擇淺復(fù)制和深復(fù)制,淺復(fù)制使用構(gòu)造方法、[:]
或copy.copy()
,深復(fù)制使用copy.deepcopy()
。del刪除的是引用,但是會導(dǎo)致對象沒有引用而被當(dāng)做垃圾回收。有時候需要保留引用而不保留對象(比如緩存),這叫做弱引用,weakref庫提供了相應(yīng)的實(shí)現(xiàn)。
參考資料:
《流暢的Python》
到此這篇關(guān)于Python基礎(chǔ)之變量的相關(guān)知識總結(jié)的文章就介紹到這了,更多相關(guān)Python變量內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python 中l(wèi)ist ,set,dict的大規(guī)模查找效率對比詳解
這篇文章主要介紹了Python 中l(wèi)ist ,set,dict的大規(guī)模查找效率對比詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10Python實(shí)現(xiàn)快速生成SQL語句的示例詳解
這篇文章主要介紹了如何使用Python中的字符串操作和數(shù)據(jù)結(jié)構(gòu),以及一些庫來自動生成SQL語句的技巧,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-04-04使用Python實(shí)現(xiàn)屏幕截圖的兩種方法
Python作為一種高效的編程語言,可以通過一些庫來實(shí)現(xiàn)對屏幕的截圖操作,本文主要介紹了使用Python實(shí)現(xiàn)屏幕截圖的兩種方法,具有一定的 參考價(jià)值,感興趣的可以了解一下2023-12-12Win10系統(tǒng)下Pytorch環(huán)境的搭建過程
今天給大家?guī)淼氖顷P(guān)于Python的相關(guān)知識,文章圍繞著Win10系統(tǒng)Pytorch環(huán)境搭建過程展開,文中有非常詳細(xì)的介紹及圖文示例,需要的朋友可以參考下2021-06-06Python實(shí)現(xiàn)的自定義多線程多進(jìn)程類示例
這篇文章主要介紹了Python實(shí)現(xiàn)的自定義多線程多進(jìn)程類,結(jié)合實(shí)例形式分析了Python多線程多進(jìn)程的相關(guān)調(diào)用與使用操作技巧,需要的朋友可以參考下2018-03-0330?個?Python?函數(shù),加速數(shù)據(jù)分析處理速度
這篇文章主要介紹了30?個?Python?函數(shù),加速數(shù)據(jù)分析處理速度,Pandas?是?Python?中最廣泛使用的數(shù)據(jù)分析和操作庫。它提供了許多功能和方法,可以加快數(shù)據(jù)分析和預(yù)處理步驟,下面我們就一起來看看這些方法吧,需要的小伙伴可以參考一下,希望給你帶來幫助2021-12-12python起點(diǎn)網(wǎng)月票榜字體反爬案例
大家好,本篇文章主要講的是python起點(diǎn)網(wǎng)月票榜字體反爬案例,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽2021-12-12