一.python 常見面試題11題

問題1

到底什麼是Python?你可以在回答中與其他技術進行對比(也鼓勵這樣做)。

答案

下面是一些關鍵點:

  • Python是一種解釋型語言。這就是說,與C語言和C的衍生語言不同,Python代碼在運行之前不需要編譯。其他解釋型語言還包括PHP和Ruby。
  • Python是動態類型語言,指的是你在聲明變量時,不需要說明變量的類型。你可以直接編寫類似x=111x="I'm a string"這樣的代碼,程序不會報錯。
  • Python非常適合面向對象的編程(OOP),因爲它支持通過組合(composition)與繼承(inheritance)的方式定義類(class)。Python中沒有訪問說明符(access specifier,類似C++中的publicprivate),這麼設計的依據是“大家都是成年人了”。
  • 在Python語言中,函數是第一類對象(first-class objects)。這指的是它們可以被指定給變量,函數既能返回函數類型,也可以接受函數作爲輸入。類(class)也是第一類對象。
  • Python代碼編寫快,但是運行速度比編譯語言通常要慢。好在Python允許加入基於C語言編寫的擴展,因此我們能夠優化代碼,消除瓶頸,這點通常是可以實現的。numpy就是一個很好地例子,它的運行速度真的非常快,因爲很多算術運算其實並不是通過Python實現的。
  • Python用途非常廣泛——網絡應用,自動化,科學建模,大數據應用,等等。它也常被用作“膠水語言”,幫助其他語言和組件改善運行狀況。

  • Python讓困難的事情變得容易,因此程序員可以專注於算法和數據結構的設計,而不用處理底層的細節。

問題2

補充缺失的代碼

def print_directory_contents(sPath):
    """
    這個函數接受文件夾的名稱作爲輸入參數,
    返回該文件夾中文件的路徑,
    以及其包含文件夾中文件的路徑。

    """
    # 補充代碼

答案

def print_directory_contents(sPath):
    import os                                       
    for sChild in os.listdir(sPath):                
        sChildPath = os.path.join(sPath,sChild)
        if os.path.isdir(sChildPath):
            print_directory_contents(sChildPath)
        else:
            print sChildPath

問題3

閱讀下面的代碼,寫出A0,A1至An的最終值。

A0 = dict(zip(('a','b','c','d','e'),(1,2,3,4,5)))
A1 = range(10)
A2 = [i for i in A1 if i in A0]
A3 = [A0[s] for s in A0]
A4 = [i for i in A1 if i in A3]
A5 = {i:i*i for i in A1}
A6 = [[i,i*i] for i in A1]

答案

A0 = {'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4}
A1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
A2 = []
A3 = [1, 3, 2, 5, 4]
A4 = [1, 2, 3, 4, 5]
A5 = {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
A6 = [[0, 0], [1, 1], [2, 4], [3, 9], [4, 16], [5, 25], [6, 36], [7, 49], [8, 64], [9, 81]]

問題4

Python和多線程(multi-threading)。這是個好主意碼?列舉一些讓Python代碼以並行方式運行的方法。

答案

Python並不支持真正意義上的多線程。Python中提供了多線程包,但是如果你想通過多線程提高代碼的速度,使用多線程包並不是個好主意。Python中有一個被稱爲Global Interpreter Lock(GIL)的東西,它會確保任何時候你的多個線程中,只有一個被執行。線程的執行速度非常之快,會讓你誤以爲線程是並行執行的,但是實際上都是輪流執行。經過GIL這一道關卡處理,會增加執行的開銷。這意味着,如果你想提高代碼的運行速度,使用threading包並不是一個很好的方法。

不過還是有很多理由促使我們使用threading包的。如果你想同時執行一些任務,而且不考慮效率問題,那麼使用這個包是完全沒問題的,而且也很方便。但是大部分情況下,並不是這麼一回事,你會希望把多線程的部分外包給操作系統完成(通過開啓多個進程),或者是某些調用你的Python代碼的外部程序(例如Spark或Hadoop),又或者是你的Python代碼調用的其他代碼(例如,你可以在Python中調用C函數,用於處理開銷較大的多線程工作)。

問題5

你如何管理不同版本的代碼?

答案

版本管理!被問到這個問題的時候,你應該要表現得很興奮,甚至告訴他們你是如何使用Git(或是其他你最喜歡的工具)追蹤自己和奶奶的書信往來。我偏向於使用Git作爲版本控制系統(VCS),但還有其他的選擇,比如subversion(SVN)。

問題6

下面代碼會輸出什麼:

def f(x,l=[]):
    for i in range(x):
        l.append(i*i)
    print l

f(2)
f(3,[3,2,1])
f(3)

答案

[0, 1]
[3, 2, 1, 0, 1, 4]
[0, 1, 0, 1, 4]

呃?

第一個函數調用十分明顯,for循環先後將0和1添加至了空列表l中。l是變量的名字,指向內存中存儲的一個列表。第二個函數調用在一塊新的內存中創建了新的列表。l這時指向了新生成的列表。之後再往新列表中添加0、1、2和4。很棒吧。第三個函數調用的結果就有些奇怪了。它使用了之前內存地址中存儲的舊列表。這就是爲什麼它的前兩個元素是0和1了。

問題7

“猴子補丁”(monkey patching)指的是什麼?這種做法好嗎?

答案

“猴子補丁”就是指,在函數或對象已經定義之後,再去改變它們的行爲。

舉個例子:

import datetime
datetime.datetime.now = lambda: datetime.datetime(2012, 12, 12)

大部分情況下,這是種很不好的做法 - 因爲函數在代碼庫中的行爲最好是都保持一致。打“猴子補丁”的原因可能是爲了測試。mock包對實現這個目的很有幫助。

問題8

這兩個參數是什麼意思:*args**kwargs?我們爲什麼要使用它們?

答案

如果我們不確定要往函數中傳入多少個參數,或者我們想往函數中以列表和元組的形式傳參數時,那就使要用*args;如果我們不知道要往函數中傳入多少個關鍵詞參數,或者想傳入字典的值作爲關鍵詞參數時,那就要使用**kwargsargskwargs這兩個標識符是約定俗成的用法,你當然還可以用*bob**billy,但是這樣就並不太妥。

問題9

下面這些是什麼意思:@classmethod@staticmethod@property

回答背景知識

這些都是裝飾器(decorator)。裝飾器是一種特殊的函數,要麼接受函數作爲輸入參數,並返回一個函數,要麼接受一個類作爲輸入參數,並返回一個類。@標記是語法糖(syntactic sugar),可以讓你以簡單易讀得方式裝飾目標對象。

問題10

按我們對super的理解,從圖中可以看出,在調用類C的初始化函數時,應該是調用類A的初始化函數,但事實上卻調用了類D的初始化函數。好一個詭異的問題!就是說,mro中記錄了一個類的所有基類的類類型序列。查看mro的記錄,發覺包含7個元素,7個類名分別爲:F E B C D A object

重點會畫邏輯圖

問題11

簡要描述Python的垃圾回收機制(garbage collection)。

答案

這裏能說的很多。你應該提到下面幾個主要的點:

  • Python在內存中存儲了每個對象的引用計數(reference count)。如果計數值變成0,那麼相應的對象就會消失,分配給該對象的內存就會釋放出來用作他用。
  • 偶爾也會出現引用循環(reference cycle)。垃圾回收器會定時尋找這個循環,並將其回收。舉個例子,假設有兩個對象o1o2,而且符合o1.x == o2o2.x == o1這兩個條件。如果o1o2沒有其他代碼引用,那麼它們就不應該繼續存在。但它們的引用計數都是1。
  • Python中使用了某些啓發式算法(heuristics)來加速垃圾回收。例如,越晚創建的對象更有可能被回收。對象被創建之後,垃圾回收器會分配它們所屬的代(generation)。每個對象都會被分配一個代,而被分配更年輕代的對象是優先被處理的。


發佈了45 篇原創文章 · 獲贊 8 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章