python面試

問:爲什麼要使用函數裝飾器?舉個例子。

裝飾器本質上是一個可調用的Python對象,用於修改或擴展函數或類定義。裝飾器的優點之一是單個裝飾器定義可以應用於多個功能(或類)。因此,裝飾器可以實現很多,否則需要大量的樣板(甚至更糟糕的冗餘!)代碼。 例如,Flask使用裝飾器作爲向Web應用程序添加新端點的機制。裝飾器的一些更常見用途的示例包括向類或函數添加同步,類型實施,日誌記錄或前/後條件。

問:什麼是lambda表達式,列表推導和生成器表達式?每個的優點和適當用途是什麼?

Lambda表達式是一種用於創建單行匿名函數的簡寫技術。它們簡單,內聯的特性 - 儘管並非總是如此 - 導致代碼比正式函數聲明的替代方案更易讀和簡潔。另一方面,根據定義,他們簡潔的內聯性質極大地限制了他們的能力和適用性。作爲匿名和內聯,在代碼中的多個位置使用相同lambda函數的唯一方法是冗餘地指定它。

列表推導提供了創建列表的簡明語法。列表推導通常用於製作列表,其中每個元素是應用於另一個序列或可迭代的每個成員的一些操作的結果。它們還可用於創建其成員滿足特定條件的元素的子序列。在Python中,列表推導提供了使用內置函數map()和filter()函數的替代方法。

由於lambda表達式和列表推導的應用使用可能會重疊,因此對於何時何地使用其中一個與另一個相比,意見差別很大。有一點要記住,雖然是一個列表理解的執行速度比採用同類解決方案有所加快map和lambda(一些快速測試產生了大約10%的性能差異)。這是因爲調用lambda函數會創建一個新的堆棧幀,而列表推導中的表達式將在不進行此操作的情況下進行計算。

生成器表達式在語法和功能上類似於列表推導,但兩者的運行方式之間存在一些相當顯着的差異,因此,應該使用每種方式。簡而言之,迭代生成器表達式或列表推導將基本上做同樣的事情,但列表推導將首先在內存中創建整個列表,而生成器表達式將根據需要動態創建項目。因此,生成器表達式可以用於非常大(甚至無限)的序列,並且它們的延遲(即按需)生成值會導致性能提高和內存使用率降低。但值得注意的是,標準Python列表方法可以用於列表推導的結果,但不能直接用於生成器表達式的結果。

問:考慮以下兩種方法來初始化數組和將產生的數組。結果數組將如何不同?爲什麼要使用一種初始化方法而不是另一種?

INITIALIZING AN ARRAY – METHOD 1

x = [[1,2,3,4]] * 3
x
[[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]

INITIALIZING AN ARRAY – METHOD 2

y = [[1,2,3,4] for _ in range(3)]
y
[[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]

WHICH METHOD SHOULD YOU USE AND WHY?

雖然兩種方法在第一時間出現以產生相同的結果,但兩者之間存在極其顯着的差異。正如您所料,方法2生成一個包含3個元素的數組,每個元素本身就是一個獨立的4元素數組。但是,在方法1中,數組的成員都指向同一個對象。這可能導致最可能出現意外和不期望的行爲,如下所示。

MODIFYING THE x ARRAY FROM THE PRIOR CODE SNIPPET:

x[0][3] = 99
x
[[1, 2, 3, 99], [1, 2, 3, 99], [1, 2, 3, 99]]

UH-OH, DON’T THINK YOU WANTED THAT TO HAPPEN!

MODIFYING THE y ARRAY FROM THE PRIOR CODE SNIPPET:

y[0][3] = 99
y
[[1, 2, 3, 99], [1, 2, 3, 4], [1, 2, 3, 4]]

THAT’S MORE LIKE WHAT YOU EXPECTED!


問:下面的第二個附錄()聲明將打印出什麼?

def append(list=[]):
… # append the length of a list to the list
… list.append(len(list))
… return list

append([‘a’,‘b’])
[‘a’, ‘b’, 2]

append() # calling with no arg uses default list value of []
[0]

append() # but what happens when we AGAIN call append with no arg?
當函數參數的默認值是表達式時,表達式僅計算一次,而不是每次調用函數時。因此,一旦list參數初始化爲空數組,後續調用append而不指定任何參數將繼續使用最初初始化列表的同一數組。因此,這將產生以下(可能是意外的)行爲:

append() # first call with no arg uses default list value of []
[0]

append() # but then look what happens…
[0, 1]

append() # successive calls keep extending the same default list!
[0, 1, 2]

append() # and so on, and so on, and so on…
[0, 1, 2, 3]
問:如何修改上一個問題中’append’方法的實現,以避免在那裏描述的不良行爲?

append方法的以下替代實現將是避免上一個問題的答案中描述的不良行爲的多種方法之一:

def append(list=None):
… if list is None:
list = []

    # append the length of a list to the list

… list.append(len(list))
… return list

append()
[0]

append()
[0]
問:如何用一行Python代碼交換兩個變量的值?

考慮這個簡單的例子:

x = ‘X’
y = ‘Y’
在許多其他語言中,交換x和y的值需要您執行以下操作:

tmp = x
x = y
y = tmp
x, y
(‘Y’, ‘X’)
但是在Python中,可以使用單行代碼進行交換(由於隱式元組打包和解包),如下所示:

x,y = y,x
x,y
(‘Y’, ‘X’)
問:下面的陳述將打印出什麼?

flist = []
for i in range(3):
… flist.append(lambda: i)

[f() for f in flist] # what will this print out?
在Python的任何閉包中,變量都是按名稱綁定的。因此,上面的代碼行將打印出以下內容:

[2, 2, 2]
大概不是上面代碼的作者想要的!

甲解決方法是創建一個單獨的函數或通過按名稱ARGS; 例如:

flist = []
for i in range(3):
… flist.append(lambda i = i : i)

[f() for f in flist]
[0, 1, 2]
問:Python 2和3之間的主要區別是什麼?

雖然Python 2在這一點上被正式認爲是遺留的,但它的使用仍然非常廣泛,這對於開發人員識別Python 2和3之間的差異非常重要。

以下是開發人員應注意的一些主要差異:

文本和數據而不是Unicode和8位字符串。 Python 3.0使用文本和(二進制)數據的概念而不是Unicode字符串和8位字符串。最大的分歧是,在Python 3.0中混合文本和數據的任何嘗試都會引發TypeError(要安全地組合兩者,你必須解碼字節或編碼Unicode,但你需要知道正確的編碼,例如UTF-8)
這解決了天真的Python程序員長期存在的陷阱。在Python 2中,如果字符串恰好只包含7位(ASCII)字節,則混合使用Unicode和8位數據,但如果包含非ASCII值,則會得到UnicodeDecodeError。此外,異常將發生在組合點,而不是在非ASCII字符放入str對象的位置。對於新手Python程序員來說,這種行爲是混亂和驚慌的常見原因。
打印功能。 該print聲明已被替換爲print()函數
xrange - buh-bye。 xrange()不再存在(range()現在表現得像xrange()以前的行爲,除了它適用於任意大小的值)
API更改:
zip(),map()而filter()現在都返回迭代器而不是列表
dict.keys(),dict.items()而dict.values()現在回到“意見”,而不是名單
dict.iterkeys(),dict.iteritems()而dict.itervalues()不再支持
比較運算符。 排序比較操作符(<,<=,>=,>)現在提出一個TypeError例外,當操作數沒有意義的自然順序。其中一些例子包括:
這樣的表達式1 < ‘’,0 > None或者len <= len不再有效
None < None現在提出一個TypeError而不是返回False
對異構列表進行排序不再有意義 - 所有元素必須相互比較
有關Python 2和3之間差異的更多詳細信息,請參見此處。

問:Python是解釋還是編譯?

如爲什麼有這麼多蟒蛇?坦率地說,這是一個棘手的問題,因爲它是畸形的。Python本身只不過是一個接口定義(對於任何語言規範都是如此),其中有多個實現。因此,解釋或編譯“Python”的問題不適用於Python語言本身; 相反,它適用於Python規範的每個特定實現。

這個問題的答案進一步複雜化的是,在CPython(最常見的Python實現)的情況下,答案實際上是“兩者兼而有之”。具體來說,使用CPython,首先編譯代碼然後進行解釋。更準確地說,它不是預編譯爲本機機器代碼,而是預編譯爲字節碼。雖然機器代碼肯定更快,但字節碼更加便攜和安全。然後在CPython的情況下解釋字節碼(或者在PyPy的情況下,在運行時解釋和編譯爲優化的機器代碼)。

問:CPython有哪些替代實現?您何時以及爲何使用它們?

其中一個更突出的替代實現是Jython,一個用Java編寫的Python實現,它使用Java虛擬機(JVM)。當CPython生成在CPython VM上運行的字節碼時,Jython會生成Java字節碼以在JVM上運行。

另一個是IronPython,用C#編寫並以.NET堆棧爲目標。IronPython運行在Microsoft的公共語言運行時(CLR)上。

正如爲什麼有這麼多蟒蛇也指出的那樣?,完全有可能在沒有接觸過Python的非CPython實現的情況下生存,但是切換有很多好處,其中大部分依賴於你的技術堆棧。

另一個值得注意的替代實現是PyPy,其主要功能包括:

速度。 由於它的Just-in-Time(JIT)編譯器,Python程序通常在PyPy上運行得更快。
內存使用情況。 對於PyPy來說,龐大的,需要大量內存的Python程序可能會比CPython中佔用更少的空間。
兼容性。 PyPy與現有的python代碼高度兼容。它支持cffi,可以運行像Twisted和Django這樣流行的Python庫。
沙箱。 PyPy提供了以完全安全的方式運行不受信任的代碼的能力。
無堆疊模式。 PyPy默認支持無堆棧模式,爲大規模併發提供微線程。
問:你在Python中進行單元測試的方法是什麼?

對這個問題的最根本的答案主要圍繞Python的單元測試的測試框架。基本上,如果候選人在回答這個問題時沒有提及unittest,那應該是一個巨大的危險信號。

unittest支持測試自動化,共享測試的設置和關閉代碼,將測試聚合到集合中,以及測試與報告框架的獨立性。unittest模塊提供的類可以輕鬆地爲一組測試支持這些質量。

假設候選人確實提到了單元測試(如果他們不這樣做,你可能只想在那時和那裏結束訪談!),你也應該讓他們描述單元測試框架的關鍵元素; 即測試夾具,測試用例,測試套件和測試運行器。

單元測試框架的最新增加是mock。mock允許您使用模擬對象替換測試中的系統部分,並對如何使用它們進行斷言。mock現在是Python標準庫的一部分,在Python 3.3之後以unittest.mock的形式提供。

模擬的價值和力量在“Python中的模擬簡介”中得到了很好的解釋。如其中所述,系統調用是模擬的主要候選者:是否編寫腳本以彈出CD驅動器,從/ tmp移除過時緩存文件的Web服務器,或綁定到TCP端口的套接字服務器,這些調用都是不希望的單元測試中的副作用。同樣,保持單元測試的高效性和高性能意味着在自動化測試運行中保留儘可能多的“慢速代碼”,即文件系統和網絡訪問。

[注意:這個問題適用於在Java方面經驗豐富的Python開發人員。]
問:在Python與Java編碼時,要記住哪些關鍵區別?

免責聲明#1。 Java和Python之間的差異很多,可能是一個值得自己(冗長)帖子的話題。以下是兩種語言之間一些主要差異的簡要示例。

免責聲明#2。 這裏的目的不是針對Python與Java的優點展開宗教鬥爭(儘可能多的樂趣!)。相反,問題實際上只是爲了看看開發人員如何理解兩種語言之間的一些實際差異。因此,下面的列表有意避免從編程生產力的角度討論Python相對於Java的可論證的優勢。

考慮到上述兩個免責聲明,下面是在使用Python與Java編碼時要記住的一些關鍵差異的示例:

動態與靜態類型。 這兩種語言之間最大的區別之一是Java僅限於靜態類型,而Python支持動態類型化變量。
靜態與類方法。 Java中的靜態方法不會轉換爲Python類方法。
在Python中,調用類方法涉及額外的內存分配,調用靜態方法或函數則不會。
在Java中,編譯器會查找虛線名稱(例如,foo.bar.method),因此在運行時,它們中有多少並不重要。但是,在Python中,查找在運行時發生,因此“每個點計數”。
方法重載。 雖然Java要求使用不同簽名明確指定多個同名函數,但在Python中使用單個函數可以實現相同的功能,如果未由調用者指定,則該函數包含具有默認值的可選參數。
單引號與雙引號。 雖然單引號與雙引號的使用在Java中具有重要意義,但它們可以在Python中互換使用(但不是,它不允許使用雙引號開始使用相同的字符串並嘗試以單引號結束它,或者反之亦然!)。
吸氣劑和二傳手(不是!)。 Python中的getter和setter是多餘的; 相反,你應該使用內置的’property’(這就是它的用途!)。在Python中,getter和setter浪費了CPU和程序員的時間。
類是可選的。 Java要求在封閉類定義的上下文中定義每個函數,而Python沒有這樣的要求。
縮進很重要… 在Python中。這咬了很多新手Python程序員。
大圖
Python的專業知識遠遠超出了語言的技術細節。 Python專家將深入理解和理解Python的優點及其侷限性。因此,這裏有一些示例問題可以幫助評估候選人的專業知識的這個方面:

問:Python特別適用於什麼?何時使用Python是項目的“正確選擇”?

雖然喜歡和不喜歡是非常個人化的,但是“值得他或她的鹽”的開發人員將突出通常被認爲是有利的Python語言的特徵(這也有助於回答Python“特別好”的問題)。這個問題的一些更常見的有效答案包括:

由於Python語法的靈活性,易於使用和易於重構,這使其特別適用於快速原型設計。
更簡潔的代碼,再次感謝Python的語法,以及豐富的功能豐富的Python庫(隨大多數Python語言實現自由分發)。
一種動態類型和強類型語言,提供罕見的代碼靈活性組合,同時避免討厭的隱式類型轉換錯誤。
它是免費和開源的! 需要我們說更多?
關於何時使用Python是項目的“正確選擇”的問題,完整的答案還取決於與語言本身正交的一些問題,例如先前的技術投資,團隊的技能組合等等。 。雖然上面提到的問題意味着對嚴格的技術答案的興趣,但是在面試中提出這些額外問題的開發者總是會“得分更多”,因爲它表明了對“更大的圖景”的認識和敏感度。 (即,不僅僅是所採用的技術)。相反,Python始終是正確選擇的響應是一個不成熟的開發人員的明確標誌。

問:Python語言有哪些缺點?

對於初學者來說,如果你熟悉一門語言,就會知道它的缺點,所以回答諸如“沒有什麼我不喜歡它”或“它沒有任何缺點”這些都是非常有說服力的。

這個問題的兩個最常見的有效答案(絕不是詳盡無遺的清單)是:

全球口譯員鎖(GIL)。 CPython(最常見的Python實現)不是完全線程安全的。爲了支持多線程Python程序,CPython提供了一個全局鎖,必須由當前線程保存才能安全地訪問Python對象。因此,無論存在多少個線程或處理器,在任何給定時間只執行一個線程。相比之下,值得注意的是,本文前面討論的PyPy實現提供了一種無堆棧模式,支持用於大規模併發的微線程。
執行速度。 Python可能比編譯語言慢,因爲它被解釋。(好吧,有點。看看我們之前關於這個主題的討論。)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章