面試必備題[轉載]

def f1(lIn):
    l1 = sorted(lIn)
    l2 = [i for i in l1 if i<0.5]
    return [i*i for i in l2]
def f2(lIn):
    l1 = [i for i in lIn if i<0.5]
    l2 = sorted(l1)
    return [i*i for i in l2]
def f3(lIn):
    l1 = [i*i for i in lIn]
    l2 = sorted(l1)
    return [i for i in l1 if i<(0.5*0.5)]

 

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]]

 

引言python

想找一份Python開發工做嗎?那你極可能得證實本身知道如何使用Python。下面這些問題涉及了與Python相關的許多技能,問題的關注點主要是語言自己,不是某個特定的包或模塊。每個問題均可以擴充爲一個教程,若是可能的話。某些問題甚至會涉及多個領域。程序員

我以前尚未出過和這些題目同樣難的面試題,若是你能輕鬆地回答出來的話,趕忙去找份工做吧!面試

問題1算法

到底什麼是Python?你能夠在回答中與其餘技術進行對比(也鼓勵這樣作)。編程

答案網絡

下面是一些關鍵點:數據結構

        Python是一種解釋型語言。這就是說,與C語言和C的衍生語言不一樣,Python代碼在運行以前不須要編譯。其餘解釋型語言還包括PHP和Ruby。多線程

        Python是動態類型語言,指的是你在聲明變量時,不須要說明變量的類型。你能夠直接編寫相似x=111和x="I'm a string"這樣的代碼,程序不會報錯。app

        Python很是適合面向對象的編程(OOP),由於它支持經過組合(composition)與繼承(inheritance)的方式定義類(class)。Python中沒有訪問說明符(access specifier,相似C++中的public和private),這麼設計的依據是「你們都是成年人了」。dom

        在Python語言中,函數是第一類對象(first-class objects)。這指的是它們能夠被指定給變量,函數既能返回函數類型,也能夠接受函數做爲輸入。類(class)也是第一類對象。

        Python代碼編寫快,可是運行速度比編譯語言一般要慢。好在Python容許加入基於C語言編寫的擴展,所以咱們可以優化代碼,消除瓶頸,這點一般是能夠實現的。numpy就是一個很好地例子,它的運行速度真的很是快,由於不少算術運算其實並非經過Python實現的。

        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)

 

特別要注意如下幾點:

命名規範要統一。若是樣本代碼中可以看出命名規範,遵循其已有的規範。

遞歸函數須要遞歸併終止。確保你明白其中的原理,不然你將面臨無休無止的調用棧(callstack)。

咱們使用os模塊與操做系統進行交互,同時作到交互方式是能夠跨平臺的。你能夠把代碼寫成sChildPath = sPath + '/' + sChild,可是這個在Windows系統上會出錯。

熟悉基礎模塊是很是有價值的,可是別想破腦殼都背下來,記住Google是你工做中的良師益友。

若是你不明白代碼的預期功能,就大膽提問。

堅持KISS原則!保持簡單,不過腦子就能懂!

爲何提這個問題:

說明面試者對與操做系統交互的基礎知識

遞歸真是太好用啦

問題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]

 答案:

爲何提這個問題:

列表解析(list comprehension)十分節約時間,對不少人來講也是一個大的學習障礙。

若是你讀懂了這些代碼,就極可能能夠寫下正確地值。

其中部分代碼故意寫的怪怪的。由於你共事的人之中也會有怪人。

問題4

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

答案

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

        不過仍是有不少理由促使咱們使用threading包的。若是你想同時執行一些任務,並且不考慮效率問題,那麼使用這個包是徹底沒問題的,並且也很方便。可是大部分狀況下,並非這麼一回事,你會但願把多線程的部分外包給操做系統完成(經過開啓多個進程),或者是某些調用你的Python代碼的外部程序(例如Spark或Hadoop),又或者是你的Python代碼調用的其餘代碼(例如,你能夠在Python中調用C函數,用於處理開銷較大的多線程工做)。

爲何提這個問題

由於GIL就是個混帳東西(A-hole)。不少人花費大量的時間,試圖尋找本身多線程代碼中的瓶頸,直到他們明白GIL的存在。

問題5

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

答案:

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

爲何提這個問題:

由於沒有版本控制的代碼,就像沒有杯子的咖啡。有時候咱們須要寫一些一次性的、能夠隨手扔掉的腳本,這種狀況下不做版本控制不要緊。可是若是你面對的是大量的代碼,使用版本控制系統是有利的。版本控制可以幫你追蹤誰對代碼庫作了什麼操做;發現新引入了什麼bug;管理你的軟件的不一樣版本和發行版;在團隊成員中分享源代碼;部署及其餘自動化處理。它能讓你回滾到出現問題以前的版本,單憑這點就特別棒了。還有其餘的好功能。怎麼一個棒字了得!

問題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、一、2和4。很棒吧。第三個函數調用的結果就有些奇怪了。它使用了以前內存地址中存儲的舊列表。這就是爲何它的前兩個元素是0和1了。

問題7

 

「猴子補丁」(monkey patching)指的是什麼?這種作法好嗎?

答案:

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

舉個例子:

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

 

大部分狀況下,這是種很很差的作法 - 由於函數在代碼庫中的行爲最好是都保持一致。打「猴子補丁」的緣由多是爲了測試。mock包對實現這個目的頗有幫助。

爲何提這個問題?

答對這個問題說明你對單元測試的方法有必定了解。你若是提到要避免「猴子補丁」,能夠說明你不是那種喜歡花裏胡哨代碼的程序員(公司裏就有這種人,跟他們共事真是糟糕透了),而是更注重可維護性。還記得KISS原則碼?答對這個問題還說明你明白一些Python底層運做的方式,函數實際是如何存儲、調用等等。

另外:若是你沒讀過mock模塊的話,真的值得花時間讀一讀。這個模塊很是有用。

問題8

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

答案

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

下面是具體的示例:

def f(*args,**kwargs): print(args, kwargs)l = [1,2,3]t = (4,5,6)d = {'a':7,'b':8,'c':9}f()f(1,2,3) 

爲何提這個問題?

有時候,咱們須要往函數中傳入未知個數的參數或關鍵詞參數。有時候,咱們也但願把參數或關鍵詞參數儲存起來,以備之後使用。有時候,僅僅是爲了節省時間。

問題9

 

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

回答背景知識

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

@my_decorator def my_func(stuff):     do_things Is equivalent to
def my_func(stuff):     do_things
my_func = my_decorator(my_func)

你能夠在本網站上找到介紹裝飾器工做原理的教材。

真正的答案

@classmethod, @staticmethod和@property這三個裝飾器的使用對象是在類中定義的函數。下面的例子展現了它們的用法和行爲:

閱讀下面的代碼,它的輸出結果是什麼?

class A(object):
    def go(self):
        print "go A go!"
    def stop(self):
        print "stop A stop!"
    def pause(self):
        raise Exception("Not Implemented")class B(A):
    def go(self):
        super(B, self).go()
        print "go B go!"class C(A):
    def go(self):
        super(C, self).go()
        print "go C go!"
    def stop(self):
        super(C, self).stop()
        print "stop C stop!"class D(B,C):
    def go(self):
        super(D, self).go()
        print "go D go!"
    def stop(self):
        super(D, self).stop()
        print "stop D stop!"
    def pause(self):
        print "wait D wait!"class E(B,C): pass
a = A()
b = B()
c = C()
d = D()
e = E()# 說明下列代碼的輸出結果
a.go()
b.go()
c.go()
d.go()
e.go()
a.stop()
b.stop()
c.stop()
d.stop()
e.stop()
a.pause()
b.pause()
c.pause()
d.pause()
e.pause()

 

爲何提這個問題?

由於面向對象的編程真的真的很重要。不騙你。答對這道問題說明你理解了繼承和Python中super函數的用法。

問題11

閱讀下面的代碼,它的輸出結果是什麼?

爲何提這個問題?

由於對象的精髓就在於組合(composition)與對象構造(object construction)。對象須要有組合成分構成,並且得以某種方式初始化。這裏也涉及到遞歸和生成器(generator)的使用。

生成器是很棒的數據類型。你能夠只經過構造一個很長的列表,而後打印列表的內容,就能夠取得與print_all_2相似的功能。生成器還有一個好處,就是不用佔據不少內存。

有一點還值得指出,就是print_all_1會以深度優先(depth-first)的方式遍歷樹(tree),而print_all_2則是寬度優先(width-first)。有時候,一種遍歷方式比另外一種更合適。但這要看你的應用的具體狀況。

問題12

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

答案

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

Python在內存中存儲了每一個對象的引用計數(reference count)。若是計數值變成0,那麼相應的對象就會小時,分配給該對象的內存就會釋放出來用做他用。

偶爾也會出現引用循環(reference cycle)。垃圾回收器會定時尋找這個循環,並將其回收。舉個例子,假設有兩個對象o1和o2,並且符合o1.x == o2和o2.x == o1這兩個條件。若是o1和o2沒有其餘代碼引用,那麼它們就不該該繼續存在。但它們的引用計數都是1。

Python中使用了某些啓發式算法(heuristics)來加速垃圾回收。例如,越晚建立的對象更有可能被回收。對象被建立以後,垃圾回收器會分配它們所屬的代(generation)。每一個對象都會被分配一個代,而被分配更年輕代的對象是優先被處理的。

問題13

將下面的函數按照執行效率高低排序。它們都接受

由0至1之間的數字構成的列表做爲輸入。這個列表能夠很長。一個輸入列表的示例以下:[random.random() for i in range(100000)]。你如何證實本身的答案是正確的。

你有實施過我的項目嗎?

真的?

若是作過我的項目,這說明從更新本身的技能水平方面來看,你願意比最低要求付出更多的努力。若是你有維護的我的項目,工做以外也堅持編碼,那麼你的僱主就更可能把你視做爲會增值的資產。即便他們不問這個問題,我也認爲談談這個話題頗有幫助。

結語

我給出的這些問題時,有意涉及了多個領域。並且答案也是特地寫的較爲囉嗦。在編程面試中,你須要展現你對語言的理解,若是你能簡要地說清楚,那請務必那樣作。我儘可能在答案中提供了足夠的信息,即便是你以前歷來沒有了解過這些領域,你也能夠從答案中學到些東西。我但願本文可以幫助你找到滿意的工做。

相關文章
相關標籤/搜索