為什么Python工程師很少像Java工程師那樣討論垃圾回收?
1、Python是如何進(jìn)行bai內(nèi)存管理du的?
Python的內(nèi)存管理主要有三種機(jī)制:引用計(jì)數(shù)zhi機(jī)制、垃圾回收dao機(jī)制和內(nèi)存池機(jī)制。
a. 引用計(jì)數(shù)
當(dāng)給一個(gè)對(duì)象分配一個(gè)新名稱或者將一個(gè)對(duì)象放入一個(gè)容器(列表、元組或字典)時(shí),該對(duì)象的引用計(jì)數(shù)都會(huì)增加。
當(dāng)使用del對(duì)對(duì)象顯示銷毀或者引用超出作用于或者被重新賦值時(shí),該對(duì)象的引用計(jì)數(shù)就會(huì)減少。
可以使用sys.getrefcount()函數(shù)來獲取對(duì)象的當(dāng)前引用計(jì)數(shù)。多數(shù)情況下,引用計(jì)數(shù)要比我們猜測的大的 多。對(duì)于不可變數(shù)據(jù)(數(shù)字和字符串),解釋器會(huì)在程序的不同部分共享內(nèi)存,以便節(jié)約內(nèi)存。
b. 垃圾回收
當(dāng)一個(gè)對(duì)象的引用計(jì)數(shù)歸零時(shí),它將被垃圾收集機(jī)制處理掉。
當(dāng)
兩個(gè)對(duì)象a和b相互引用時(shí),del語句可以減少a和b的引用計(jì)數(shù),并銷毀用于引用底層對(duì)象的名稱。然而由于每個(gè)對(duì)象都包含一個(gè)對(duì)其他對(duì)象的應(yīng)用,因此引用
計(jì)數(shù)不會(huì)歸零,對(duì)象也不會(huì)銷毀。(從而導(dǎo)致內(nèi)存泄露)。為解決這一問題,解釋器會(huì)定期執(zhí)行一個(gè)循環(huán)檢測器,搜索不可訪問對(duì)象的循環(huán)并刪除它們。
c. 內(nèi)存池機(jī)制
Python提供了對(duì)內(nèi)存的垃圾收集機(jī)制,但是它將不用的內(nèi)存放到內(nèi)存池而不是返回給操作系統(tǒng)。
1)Pymalloc機(jī)制。為了加速Python的執(zhí)行效率,Python引入了一個(gè)內(nèi)存池機(jī)制,用于管理對(duì)小塊內(nèi)存的申請(qǐng)和釋放。
2)Python中所有小于256個(gè)字節(jié)的對(duì)象都使用pymalloc實(shí)現(xiàn)的分配器,而大的對(duì)象則使用系統(tǒng)的 malloc。
3)對(duì)于Python對(duì)象,如整數(shù),浮點(diǎn)數(shù)和List,都有其獨(dú)立的私有內(nèi)存池,對(duì)象間不共享他們的內(nèi)存池。也就是說如果你分配又釋放了大量的整數(shù),用于緩存這些整數(shù)的內(nèi)存就不能再分配給浮點(diǎn)數(shù)。
【Python環(huán)境】12道 Python面試題總結(jié)
2、什么是lambda函數(shù)?它有什么好處?
lambda 表達(dá)式,通常是在需要一個(gè)函數(shù),但是又不想費(fèi)神去命名一個(gè)函數(shù)的場合下使用,也就是指匿名函數(shù)
lambda函數(shù):首要用途是指點(diǎn)短小的回調(diào)函數(shù)
lambda [arguments]: expression
>>> a=lambda x,y:x+y
>>> a(3,11)
3、Python里面如何實(shí)現(xiàn)tuple和list的轉(zhuǎn)換?
直接使用tuple和list函數(shù)就行了,type()可以判斷對(duì)象的類型。
4、請(qǐng)寫出一段Python代碼實(shí)現(xiàn)刪除一個(gè)list里面的重復(fù)元素。
這個(gè)地方用set可以實(shí)現(xiàn)。
5、Python里面如何拷貝一個(gè)對(duì)象?(賦值,淺拷貝,深拷貝的區(qū)別)
賦值(=),就是創(chuàng)建了對(duì)象的一個(gè)新的引用,修改其中任意一個(gè)變量都會(huì)影響到另一個(gè)。
淺拷貝:創(chuàng)建一個(gè)新的對(duì)象,但它包含的是對(duì)原始對(duì)象中包含項(xiàng)的引用(如果用引用的方式修改其中一個(gè)對(duì)象,另外一個(gè)也會(huì)修改改變){1,完全切片方法;2,工廠函數(shù),如list();3,copy模塊的copy()函數(shù)}
深拷貝:創(chuàng)建一個(gè)新的對(duì)象,并且遞歸的復(fù)制它所包含的對(duì)象(修改其中一個(gè),另外一個(gè)不會(huì)改變){copy模塊的deep.deepcopy()函數(shù)}
6、介紹一下except的用法和作用?
try…except…except…[else…][finally…]
執(zhí)行try下的語句,如果引發(fā)異常,則執(zhí)行過程會(huì)跳到except語句。對(duì)每個(gè)except分支順序嘗試執(zhí)行,如果引發(fā)的異常與except中的異常組匹配,執(zhí)行相應(yīng)的語句。如果所有的except都不匹配,則異常會(huì)傳遞到下一個(gè)調(diào)用本代碼的最高層try代碼中。
try下的語句正常執(zhí)行,則執(zhí)行else塊代碼。如果發(fā)生異常,就不會(huì)執(zhí)行
如果存在finally語句,最后總是會(huì)執(zhí)行。
【Python環(huán)境】12道 Python面試題總結(jié)
7、Python里面match()和search()的區(qū)別?
re模塊中match(pattern,string [,flags]),檢查string的開頭是否與pattern匹配。
re模塊中research(pattern,string [,flags]),在string搜索pattern的第一個(gè)匹配值。
>>> print(re.match(‘super’, ‘superstition’).span())
(0, 5)
>>> print(re.match(‘super’, ‘insuperable’))
None
>>> print(re.search(‘super’, ‘superstition’).span())
(0, 5)
>>> print(re.search(‘super’, ‘insuperable’).span())
(2, 7)
8、用Python匹配HTML tag的時(shí)候,<.*>和<.*?>有什么區(qū)別?
術(shù)語叫貪婪匹配( <.*> )和非貪婪匹配( <.*?> )
例如:
test
<.*> :
test
<.*?> :
9、以下的代碼的輸出將是什么? 說出你的答案并解釋
輸出:
使你困惑或是驚奇的是關(guān)于最后一行的輸出是 3 2 3 而不是 3 2 1。為什么改變了 Parent.x 的值還會(huì)改變 Child2.x 的值,但是同時(shí) Child1.x 值卻沒有改變?
這
個(gè)答案的關(guān)鍵是,在 Python
中,類變量在內(nèi)部是作為字典處理的。如果一個(gè)變量的名字沒有在當(dāng)前類的字典中發(fā)現(xiàn),將搜索祖先類(比如父類)直到被引用的變量名被找到(如果這個(gè)被引用的
變量名既沒有在自己所在的類又沒有在祖先類中找到,會(huì)引發(fā)一個(gè) AttributeError 異常 )。
因此,在父類中設(shè)置 x = 1 會(huì)使得類變量 X 在引用該類和其任何子類中的值為 1。這就是因?yàn)榈谝粋€(gè) print 語句的輸出是 1 1 1。
隨后,如果任何它的子類重寫了該值(例如,我們執(zhí)行語句 Child1.x = 2),然后,該值僅僅在子類中被改變。這就是為什么第二個(gè) print 語句的輸出是 1 2 1。
最后,如果該值在父類中被改變(例如,我們執(zhí)行語句 Parent.x = 3),這個(gè)改變會(huì)影響到任何未重寫該值的子類當(dāng)中的值(在這個(gè)示例中被影響的子類是 Child2)。這就是為什么第三個(gè) print 輸出是 3 2 3。
10、以下代碼將輸出什么?
答案
以上代碼將輸出 [],并且不會(huì)導(dǎo)致一個(gè) IndexError。
正如人們所期望的,試圖訪問一個(gè)超過列表索引值的成員將導(dǎo)致 IndexError(比如訪問以上列表的 list[10])。盡管如此,試圖訪問一個(gè)列表的以超出列表成員數(shù)作為開始索引的切片將不會(huì)導(dǎo)致 IndexError,并且將僅僅返回一個(gè)空列表。
【Python環(huán)境】12道 Python面試題總結(jié)
一個(gè)討厭的小問題是它會(huì)導(dǎo)致出現(xiàn) bug ,并且這個(gè)問題是難以追蹤的,因?yàn)樗谶\(yùn)行時(shí)不會(huì)引發(fā)錯(cuò)誤。
11、以下的代碼的輸出將是什么? 說出你的答案并解釋?
你將如何修改 extendList 的定義來產(chǎn)生期望的結(jié)果
以上代碼的輸出為:
許多人會(huì)錯(cuò)誤的認(rèn)為 list1 應(yīng)該等于 [10] 以及 list3 應(yīng)該等于 ['a']。認(rèn)為 list 的參數(shù)會(huì)在 extendList 每次被調(diào)用的時(shí)候會(huì)被設(shè)置成它的默認(rèn)值 []。
盡管如此,實(shí)際發(fā)生的事情是,新的默認(rèn)列表僅僅只在函數(shù)被定義時(shí)創(chuàng)建一次。隨后當(dāng) extendList 沒有被指定的列表參數(shù)調(diào)用的時(shí)候,其使用的是同一個(gè)列表。這就是為什么當(dāng)函數(shù)被定義的時(shí)候,表達(dá)式是用默認(rèn)參數(shù)被計(jì)算,而不是它被調(diào)用的時(shí)候。
因此,list1 和 list3 是操作的相同的列表。而 ````list2是操作的它創(chuàng)建的獨(dú)立的列表(通過傳遞它自己的空列表作為list``` 參數(shù)的值)。
extendList 函數(shù)的定義可以做如下修改,但,當(dāng)沒有新的 list 參數(shù)被指定的時(shí)候,會(huì)總是開始一個(gè)新列表,這更加可能是一直期望的行為。
12、以下程序輸出什么?
好
吧,第一行代碼覺對(duì)是我第一次見,第一行輸出的是[[], [], [], [], []],一個(gè)含有5個(gè)空列表的列表,而第二行輸出的是[[10],
[10], [10], [10],
[10]],我只能解釋為這5個(gè)列表指向了同一個(gè)列表,所以修改任意一個(gè)其它4個(gè)都會(huì)改變,可以用list[0]=10 斷開一個(gè)連接試試。