溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Python崗位必備的面試題目

發(fā)布時(shí)間:2020-08-06 09:29:44 來源:億速云 閱讀:137 作者:小新 欄目:編程語言

小編給大家分享一下Python崗位必備的面試題目,希望大家閱讀完這篇文章后大所收獲,下面讓我們一起去探討吧!

Python面試(一)交換變量值

平時(shí)時(shí)不時(shí)會(huì)面面實(shí)習(xí)生,大多數(shù)的同學(xué)在學(xué)校里都已經(jīng)掌握了Python。面試的時(shí)候要求同學(xué)們實(shí)現(xiàn)一個(gè)簡單的函數(shù),交換兩個(gè)變量的值,大多數(shù)的同學(xué)給出的都是如下的答案:

Python崗位必備的面試題目

實(shí)際上,Python中還有更簡潔的更具Python風(fēng)格的實(shí)現(xiàn),如下:

Python崗位必備的面試題目

相比前一種方法,后一種方法節(jié)省一個(gè)中間變量,在性能上也優(yōu)于前一種方法。

我們從Python的字節(jié)碼來深入分析一下原因。

Python崗位必備的面試題目

dis是個(gè)反匯編工具,將Python代碼翻譯成字節(jié)碼指令。這里的輸出如下:

Python崗位必備的面試題目

通過字節(jié)碼可以看到,swap1和swap2最大的區(qū)別在于,swap1中通過ROT_TWO交換棧頂?shù)膬蓚€(gè)元素實(shí)現(xiàn)x和y值的互換,swap2中引入了tmp變量,多了一次LOAD_FAST, STORE_FAST的操作。執(zhí)行一個(gè)ROT_TWO指令比執(zhí)行一個(gè)LOAD_FAST+STORE_FAST的指令快,這也是為什么swap1比swap2性能更好的原因。

Python面試(二) is 和 == 的區(qū)別

面試實(shí)習(xí)生的時(shí)候,當(dāng)問到 is 和 == 的區(qū)別時(shí),很多同學(xué)都答不上來,搞不清兩者什么時(shí)候返回一致,什么時(shí)候返回不一致。本文我們來看一下這兩者的區(qū)別。

我們先來看幾個(gè)例子:

Python崗位必備的面試題目

上面的輸出結(jié)果中為什么有的 is 和 == 的結(jié)果相同,有的不相同呢?我們來看下官方文檔中對于 is 和 == 的解釋。

官方文檔中說 is 表示的是對象標(biāo)示符(object identity),而 == 表示的是相等(equality)。is 的作用是用來檢查對象的標(biāo)示符是否一致,也就是比較兩個(gè)對象在內(nèi)存中的地址是否一樣,而 == 是用來檢查兩個(gè)對象是否相等。

我們在檢查 a is b 的時(shí)候,其實(shí)相當(dāng)于檢查 id(a) == id(b)。而檢查 a == b 的時(shí)候,實(shí)際是調(diào)用了對象 a 的 __eq()__ 方法,a == b 相當(dāng)于 a.__eq__(b)。

一般情況下,如果 a is b 返回True的話,即 a 和 b 指向同一塊內(nèi)存地址的話,a == b 也返回True,即 a 和 b 的值也相等。

好了,看明白上面的解釋后,我們來看下前面的幾個(gè)例子:

Python崗位必備的面試題目

打印出 id(a) 和 id(b) 后就很清楚了。只要 a 和 b 的值相等,a == b 就會(huì)返回True,而只有 id(a) 和 id(b) 相等時(shí),a is b 才返回 True。

這里還有一個(gè)問題,為什么 a 和 b 都是 "hello" 的時(shí)候,a is b 返回True,而 a 和 b都是 "hello world" 的時(shí)候,a is b 返回False呢?

這是因?yàn)榍耙环N情況下Python的字符串駐留機(jī)制起了作用。對于較小的字符串,為了提高系統(tǒng)性能Python會(huì)保留其值的一個(gè)副本,當(dāng)創(chuàng)建新的字符串的時(shí)候直接指向該副本即可。所以 "hello" 在內(nèi)存中只有一個(gè)副本,a 和 b 的 id 值相同,而 "hello world" 是長字符串,不駐留內(nèi)存,Python中各自創(chuàng)建了對象來表示 a 和 b,所以他們的值相同但 id 值不同。

同學(xué)指出:intern機(jī)制和字符串長短無關(guān),在交互模式下,每行字符串字面量都會(huì)申請一個(gè)新字符串,但是只含大小寫字母、數(shù)字和下劃線的會(huì)被intern,也就是維護(hù)了一張dict來使得這些字符串全局唯一)

總結(jié)一下,is 是檢查兩個(gè)對象是否指向同一塊內(nèi)存空間,而 == 是檢查他們的值是否相等??梢钥闯?,is 是比 == 更嚴(yán)格的檢查,is 返回True表明這兩個(gè)對象指向同一塊內(nèi)存,值也一定相同。

看到這里,大家是不是搞懂了 is 和 == 的區(qū)別呢?

讓我們深入一步來思考一下下面這個(gè)問題:

Python里和None比較時(shí),為什么是 is None 而不是 == None 呢?

Python面試(三)可變對象和不可變對象

上一個(gè)面試題:Python面試之 is 和 == 的區(qū)別的最后留了一個(gè)問題:

Python里和None比較時(shí),為什么是 is None 而不是 == None 呢?

這是因?yàn)镹one在Python里是個(gè)單例對象,一個(gè)變量如果是None,它一定和None指向同一個(gè)內(nèi)存地址。而 == None背后調(diào)用的是__eq__,而__eq__可以被重載,下面是一個(gè) is not None但 == None的例子:

Python崗位必備的面試題目

好了,解答就到這里,我們開始本篇的正題。

Python中有可變對象和不可變對象之分??勺儗ο髣?chuàng)建后可改變但地址不會(huì)改變,即變量指向的還是原來的變量;不可變對象創(chuàng)建之后便不能改變,如果改變則會(huì)指向一個(gè)新的對象。

Python中dict、list是可變對象,str、int、tuple、float是不可變對象。

來看一個(gè)字符串的例子:

Python崗位必備的面試題目

上面的例子里,修改a指向的對象的值會(huì)導(dǎo)致拋出異常。

執(zhí)行 a = a + " world"時(shí),先計(jì)算等號右邊的表達(dá)式,生成一個(gè)新的對象賦值到變量a,因此a指向的對象發(fā)生了改變,id(a) 的值也與原先不同。

再來看一個(gè)列表的例子:

Python崗位必備的面試題目

上面對a修改元素、添加元素,變量a還是指向原來的對象。

將a賦值給b后,變量b和a都指向同一個(gè)對象,因此修改b的元素值也會(huì)影響a。

變量c是對b的切片操作的返回值,切片操作相當(dāng)于淺拷貝,會(huì)生成一個(gè)新的對象,因此c指向的對象不再是b所指向的對象,對c的操作不會(huì)改變b的值。

理解了上面不可變對象和可變對象的區(qū)別后,我們再來看一個(gè)有趣的問題:

Python崗位必備的面試題目

明明group1和group2是不同的對象(id值不同),為什么調(diào)用group2的add_member方法會(huì)影響group1的members?

其中的奧妙就在于__init__函數(shù)的第二個(gè)參數(shù)是默認(rèn)參數(shù),默認(rèn)參數(shù)的默認(rèn)值在函數(shù)創(chuàng)建的時(shí)候就生成了,每次調(diào)用都是用了這個(gè)對象的緩存。我們檢查id(group1.mebers)和id(group2.members),可以發(fā)現(xiàn)他們是相同的。

print(id(group1.members)) # 輸出 140127132522040
print(id(group2.members)) # 輸出 140127132522040

所以,group1.members和group2.members指向了同一個(gè)對象,對group2.members的修改也會(huì)影響group1.members。

那么問題來了,怎樣修改代碼才能解決上面默認(rèn)參數(shù)的問題呢?

Python面試(四)連接字符串用join還是+

上一個(gè)面試題:Python面試之可變對象和不可變對象的最后留了一個(gè)問題:

Python崗位必備的面試題目

上述代碼中默認(rèn)參數(shù)值對象會(huì)被緩存,造成Group類型的對象共享同一個(gè)members列表,怎樣才能解決這個(gè)問題呢?

其實(shí)很簡單,只要傳入None作為默認(rèn)參數(shù),在創(chuàng)建對象的時(shí)候動(dòng)態(tài)生成列表,如下:

Python崗位必備的面試題目

這樣對于不同的group對象,它們的members也是不同的對象,所以不會(huì)再出現(xiàn)更新一個(gè)group對象的members也會(huì)更新另外一個(gè)group對象的members了。

本篇要講的是,連接字符串的時(shí)候可以用join也可以用+,但這兩者有沒有區(qū)別呢?

我們先來看一下用join和+連接字符串的例子:

Python崗位必備的面試題目

兩者的結(jié)果是一樣,那么考慮這樣一個(gè)問題,這兩者在性能上有區(qū)別嗎?

我們來做個(gè)實(shí)驗(yàn),比較下join和+的性能:

Python崗位必備的面試題目

上面的程序有如下的輸出:

join: 0.116944, plus: 0.394379

可以看到,join的性能明顯好于+。這是為什么呢?

原因是這樣的,上一篇Python面試之可變對象和不可變對象中講過字符串是不可變對象,當(dāng)用操作符+連接字符串的時(shí)候,每執(zhí)行一次+都會(huì)申請一塊新的內(nèi)存,然后復(fù)制上一個(gè)+操作的結(jié)果和本次操作的右操作符到這塊內(nèi)存空間,因此用+連接字符串的時(shí)候會(huì)涉及好幾次內(nèi)存申請和復(fù)制。而join在連接字符串的時(shí)候,會(huì)先計(jì)算需要多大的內(nèi)存存放結(jié)果,然后一次性申請所需內(nèi)存并將字符串復(fù)制過去,這是為什么join的性能優(yōu)于+的原因。所以在連接字符串?dāng)?shù)組的時(shí)候,我們應(yīng)考慮優(yōu)先使用join。

Python面試(五)理解__new__和__init__的區(qū)別

很多同學(xué)都以為Python中的__init__是構(gòu)造方法,但其實(shí)不然,Python中真正的構(gòu)造方法是__new__。__init__和__new__有什么區(qū)別?本文就來探討一下。

我們先來看一下__init__的用法:

Python崗位必備的面試題目

上面的代碼會(huì)輸出如下的結(jié)果:

Python崗位必備的面試題目

那么我們思考一個(gè)問題,Python中要實(shí)現(xiàn)Singleton怎么實(shí)現(xiàn),要實(shí)現(xiàn)工廠模式怎么實(shí)現(xiàn)?

用__init__函數(shù)似乎沒法做到呢~

實(shí)際上,__init__函數(shù)并不是真正意義上的構(gòu)造函數(shù),__init__方法做的事情是在對象創(chuàng)建好之后初始化變量。真正創(chuàng)建實(shí)例的是__new__方法。

我們來看下面的例子:

Python崗位必備的面試題目

上面的代碼輸出如下的結(jié)果:

Python崗位必備的面試題目

上面的代碼中實(shí)例化了一個(gè)Person對象,可以看到__new__和__init__都被調(diào)用了。__new__方法用于創(chuàng)建對象并返回對象,當(dāng)返回對象時(shí)會(huì)自動(dòng)調(diào)用__init__方法進(jìn)行初始化。__new__方法是靜態(tài)方法,而__init__是實(shí)例方法。

好了,理解__new__和__init__的區(qū)別后,我們再來看一下前面提出的問題,用Python怎么實(shí)現(xiàn)Singleton,怎么實(shí)現(xiàn)工廠模式?

先來看Singleton:

Python崗位必備的面試題目

上面的代碼輸出:

Python崗位必備的面試題目

可以看到s1和s2都指向同一個(gè)對象,實(shí)現(xiàn)了單例模式。

再來看下工廠模式的實(shí)現(xiàn):

Python崗位必備的面試題目

上面的代碼輸出:

Python崗位必備的面試題目

看完上面兩個(gè)例子,大家是不是對__new__和__init__的區(qū)別有了更深入的理解?

Python面試(六)with與上下文管理器With基本語法

Python老司機(jī)應(yīng)該對下面的語法不陌生:

Python崗位必備的面試題目

上面的代碼往output文件寫入了Hello world字符串,with語句會(huì)在執(zhí)行完代碼塊后自動(dòng)關(guān)閉文件。這里無論寫文件的操作成功與否,是否有異常拋出,with語句都會(huì)保證文件被關(guān)閉。

如果不用with,我們可能要用下面的代碼實(shí)現(xiàn)類似的功能:

Python崗位必備的面試題目

可以看到使用了with的代碼比上面的代碼簡潔許多。

上面的with代碼背后發(fā)生了些什么?我們來看下它的執(zhí)行流程:

首先執(zhí)行open('output', 'w'),返回一個(gè)文件對象;

調(diào)用這個(gè)文件對象的__enter__方法,并將__enter__方法的返回值賦值給變量f;

執(zhí)行with語句體,即with語句包裹起來的代碼塊;

不管執(zhí)行過程中是否發(fā)生了異常,執(zhí)行文件對象的__exit__方法,在__exit__方法中關(guān)閉文件。

這里的關(guān)鍵在于open返回的文件對象實(shí)現(xiàn)了__enter__和__exit__方法。一個(gè)實(shí)現(xiàn)了__enter__和__exit__方法的對象就稱之為上下文管理器。

上下文管理器

上下文管理器定義執(zhí)行 with 語句時(shí)要建立的運(yùn)行時(shí)上下文,負(fù)責(zé)執(zhí)行 with 語句塊上下文中的進(jìn)入與退出操作。__enter__方法在語句體執(zhí)行之前進(jìn)入運(yùn)行時(shí)上下文,__exit__在語句體執(zhí)行完后從運(yùn)行時(shí)上下文退出。

在實(shí)際應(yīng)用中,__enter__一般用于資源分配,如打開文件、連接數(shù)據(jù)庫、獲取線程鎖;__exit__一般用于資源釋放,如關(guān)閉文件、關(guān)閉數(shù)據(jù)庫連接、釋放線程鎖。

自定義上下文管理器

既然上下文管理器就是實(shí)現(xiàn)了__enter__和__exit__方法的對象,我們能不能定義自己的上下文管理器呢?答案是肯定的。

我們先來看下__enter__和__exit__方法的定義:

__enter__() - 進(jìn)入上下文管理器的運(yùn)行時(shí)上下文,在語句體執(zhí)行前調(diào)用。如果有as子句,with語句將該方法的返回值賦值給 as 子句中的 target。

__exit__(exception_type, exception_value, traceback) - 退出與上下文管理器相關(guān)的運(yùn)行時(shí)上下文,返回一個(gè)布爾值表示是否對發(fā)生的異常進(jìn)行處理。如果with語句體中沒有異常發(fā)生,則__exit__的3個(gè)參數(shù)都為None,即調(diào)用 __exit__(None, None, None),并且__exit__的返回值直接被忽略。如果有發(fā)生異常,則使用 sys.exc_info 得到的異常信息為參數(shù)調(diào)用__exit__(exception_type, exception_value, traceback)。出現(xiàn)異常時(shí),如果__exit__(exception_type, exception_value, traceback)返回 False,則會(huì)重新拋出異常,讓with之外的語句邏輯來處理異常;如果返回 True,則忽略異常,不再對異常進(jìn)行處理。

理解了__enter__和__exit__方法后,我們來自己定義一個(gè)簡單的上下文管理器。這里不做實(shí)際的資源分配和釋放,而用打印語句來表明當(dāng)前的操作。

Python崗位必備的面試題目

運(yùn)行上面的代碼,會(huì)得到如下的輸出:

Python崗位必備的面試題目

我們在with語句體中人為地拋出一個(gè)異常:

Python崗位必備的面試題目

會(huì)得到如下的輸出:

Python崗位必備的面試題目

如我們所期待,with語句體中拋出異常,__exit__方法中exception_type不為None,__exit__方法返回False,異常被重新拋出。

以上,我們通過實(shí)現(xiàn)__enter__和__exit__方法來實(shí)現(xiàn)了一個(gè)自定義的上下文管理器。

contextlib庫

除了上面的方法,我們也可以使用contextlib庫來自定義上下文管理器。如果用contextlib來實(shí)現(xiàn),可以用下面的代碼來實(shí)現(xiàn)類似的上下文管理器:

Python崗位必備的面試題目

上面的代碼涉及到裝飾器(@contextmanager),生成器(yield),有點(diǎn)難讀。這里yield之前的代碼相當(dāng)于__enter__方法,在進(jìn)入with語句體之前執(zhí)行,yield之后的代碼相當(dāng)于__exit__方法,在退出with語句體的時(shí)候執(zhí)行。

Python面試(七)你真的理解finally了嗎?

無論try語句中是否拋出異常,finally中的語句一定會(huì)被執(zhí)行。我們來看下面的例子:

Python崗位必備的面試題目

不論try中寫文件的過程中是否有異常,finally中關(guān)閉文件的操作一定會(huì)執(zhí)行。由于finally的這個(gè)特性,finally經(jīng)常被用來做一些清理工作。

我們再來看下面的例子:

Python崗位必備的面試題目

這個(gè)例子中 func1() 和 func2() 返回什么呢?

答案是 func1() 返回2, func2() 返回3。為什么是這樣的呢?我們先來看一段Python官網(wǎng)上對于finally的解釋:

A finally clause is always executed before leaving the try statement, whether an exception has occurred or not. When an exception has occurred in the try clause and has not been handled by an except clause (or it has occurred in a except or else clause), it is re-raised after the finally clause has been executed. The finally clause is also executed “on the way out” when any other clause of the try statement is left via a break, continue or return statement.

重點(diǎn)部分用粗體標(biāo)出了,翻成中文就是try塊中包含break、continue或者return語句的,在離開try塊之前,finally中的語句也會(huì)被執(zhí)行。

所以在上面的例子中,func1() 中,在try塊return之前,會(huì)執(zhí)行finally中的語句,try中的return被忽略了,最終返回的值是finally中return的值。func2() 中,try塊中拋出異常,被except捕獲,在except塊return之前,執(zhí)行finally中的語句,except中的return被忽略,最終返回的值是finally中return的值。

我們在上面的例子中加入print語句,可以更清楚地看到過程:

Python崗位必備的面試題目

上面的代碼輸出:

Python崗位必備的面試題目

我們對上面的func2做一些修改,如下:

Python崗位必備的面試題目

輸出如下:

Python崗位必備的面試題目

try中拋出的異常是ValueError類型的,而except中定位的是IndexError類型的,try中拋出的異常沒有被捕獲到,所以except中的語句沒有被執(zhí)行,但不論異常有沒有被捕獲,finally還是會(huì)執(zhí)行,最終函數(shù)返回了finally中的返回值3。

這里還可以看到另外一個(gè)問題。try中拋出的異常沒有被捕獲到,按理說當(dāng)finally執(zhí)行完畢后,應(yīng)該被再次拋出,但finally里執(zhí)行了return,導(dǎo)致異常被丟失。

可以看到在finally中使用return會(huì)導(dǎo)致很多問題。實(shí)際應(yīng)用中,不推薦在finally中使用return返回。

看完了這篇文章,相信你對Python崗位必備的面試題目有了一定的了解,想了解更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI