5 個很好的 Python 面試題

注:本文的原文是 5 Great Python Interview Questions,同時謝謝 @非烏龜 指出個人疏漏,沒有來源標記,也贊其細心,但願看文章的同時你們都能看下原文,由於每一個人的理解不一致,原汁原味的最有幫助,我翻譯不少文章的目的一是爲了本身之後找資料方便;二是做爲一個索引,之後再看原文的時候,能更加快捷。其目的仍是但願你們能看原文的。python

問題一:如下的代碼的輸出將是什麼? 說出你的答案並解釋。

class Parent(object):
    x = 1

class Child1(Parent):
    pass

class Child2(Parent):
    pass

print Parent.x, Child1.x, Child2.x
Child1.x = 2
print Parent.x, Child1.x, Child2.x
Parent.x = 3
print Parent.x, Child1.x, Child2.x

答案算法

以上代碼的輸出是:編程

1 1 1
1 2 1
3 2 3

使你困惑或是驚奇的是關於最後一行的輸出是 3 2 3 而不是 3 2 1。爲何改變了 Parent.x 的值還會改變 Child2.x 的值,可是同時 Child1.x 值卻沒有改變?閉包

這個答案的關鍵是,在 Python 中,類變量在內部是做爲字典處理的。若是一個變量的名字沒有在當前類的字典中發現,將搜索祖先類(好比父類)直到被引用的變量名被找到(若是這個被引用的變量名既沒有在本身所在的類又沒有在祖先類中找到,會引起一個 AttributeError 異常 )。app

所以,在父類中設置 x = 1 會使得類變量 X 在引用該類和其任何子類中的值爲 1。這就是由於第一個 print 語句的輸出是 1 1 1編程語言

隨後,若是任何它的子類重寫了該值(例如,咱們執行語句 Child1.x = 2),而後,該值僅僅在子類中被改變。這就是爲何第二個 print 語句的輸出是 1 2 1ide

最後,若是該值在父類中被改變(例如,咱們執行語句 Parent.x = 3),這個改變會影響到任何未重寫該值的子類當中的值(在這個示例中被影響的子類是 Child2)。這就是爲何第三個 print 輸出是 3 2 3函數

問題二:如下的代碼的輸出將是什麼? 說出你的答案並解釋?

def div1(x,y):
    print("%s/%s = %s" % (x, y, x/y))

def div2(x,y):
    print("%s//%s = %s" % (x, y, x//y))

div1(5,2)
div1(5.,2)
div2(5,2)
div2(5.,2.)

答案ui

這個答案實際依賴於你使用的是 Python 2 仍是 Python 3。翻譯

在 Python 3 中,指望的輸出是:

5/2 = 2.5
5.0/2 = 2.5
5//2 = 2
5.0//2.0 = 2.0

在 Python 2 中,儘管如此,以上代碼的輸出將是:

5/2 = 2
5.0/2 = 2.5
5//2 = 2
5.0//2.0 = 2.0

默認,若是兩個操做數都是整數,Python 2 自動執行整型計算。結果,5/2 值爲 2,然而 5./2 值爲 ```2.5``。

注意,儘管如此,你能夠在 Python 2 中重載這一行爲(好比達到你想在 Python 3 中的一樣結果),經過添加如下導入:

from __future__ import division

也須要注意的是「雙劃線」(//)操做符將一直執行整除,而無論操做數的類型,這就是爲何 5.0//2.0 值爲 2.0

注: 在 Python 3 中,/ 操做符是作浮點除法,而 // 是作整除(即商沒有餘數,好比 10 // 3 其結果就爲 3,餘數會被截除掉,而 (-7) // 3 的結果倒是 -3。這個算法與其它不少編程語言不同,須要注意,它們的整除運算會向0的方向取值。而在 Python 2 中,/ 就是整除,即和 Python 3 中的 // 操做符同樣,)

問題三:如下代碼將輸出什麼?

list = ['a', 'b', 'c', 'd', 'e']
print list[10:]

答案

以上代碼將輸出 [],而且不會致使一個 IndexError

正如人們所指望的,試圖訪問一個超過列表索引值的成員將致使 IndexError(好比訪問以上列表的 list[10])。儘管如此,試圖訪問一個列表的以超出列表成員數做爲開始索引的切片將不會致使 IndexError,而且將僅僅返回一個空列表。

一個討厭的小問題是它會致使出現 bug ,而且這個問題是難以追蹤的,由於它在運行時不會引起錯誤。

問題四:如下的代碼的輸出將是什麼? 說出你的答案並解釋?

def multipliers():
    return [lambda x : i * x for i in range(4)]

print [m(2) for m in multipliers()]

你將如何修改 multipliers 的定義來產生指望的結果

答案

以上代碼的輸出是 [6, 6, 6, 6] (而不是 [0, 2, 4, 6])。

這個的緣由是 Python 的閉包的後期綁定致使的 late binding,這意味着在閉包中的變量是在內部函數被調用的時候被查找。因此結果是,當任何 multipliers() 返回的函數被調用,在那時,i 的值是在它被調用時的周圍做用域中查找,到那時,不管哪一個返回的函數被調用,for 循環都已經完成了,i 最後的值是 3,所以,每一個返回的函數 multiplies 的值都是 3。所以一個等於 2 的值被傳遞進以上代碼,它們將返回一個值 6 (好比: 3 x 2)。

(順便說下,正如在 The Hitchhiker’s Guide to Python 中指出的,這裏有一點廣泛的誤解,是關於 lambda 表達式的一些東西。一個 lambda 表達式建立的函數不是特殊的,和使用一個普通的 def 建立的函數展現的表現是同樣的。)

這裏有兩種方法解決這個問題。

最廣泛的解決方案是建立一個閉包,經過使用默認參數當即綁定它的參數。例如:

def multipliers():
    return [lambda x, i=i : i * x for i in range(4)]

另一個選擇是,你可使用 functools.partial 函數:

from functools import partial
from operator import mul

def multipliers():
    return [partial(mul, i) for i in range(4)]

問題五:如下的代碼的輸出將是什麼? 說出你的答案並解釋?

def extendList(val, list=[]):
    list.append(val)
    return list

list1 = extendList(10)
list2 = extendList(123,[])
list3 = extendList('a')

print "list1 = %s" % list1
print "list2 = %s" % list2
print "list3 = %s" % list3

你將如何修改 extendList 的定義來產生指望的結果

以上代碼的輸出爲:

list1 = [10, 'a']
list2 = [123]
list3 = [10, 'a']

許多人會錯誤的認爲 list1 應該等於 [10] 以及 list3 應該等於 ['a']。認爲 list 的參數會在 extendList 每次被調用的時候會被設置成它的默認值 []

儘管如此,實際發生的事情是,新的默認列表僅僅只在函數被定義時建立一次。隨後當 extendList 沒有被指定的列表參數調用的時候,其使用的是同一個列表。這就是爲何當函數被定義的時候,表達式是用默認參數被計算,而不是它被調用的時候。

所以,list1list3 是操做的相同的列表。而 ````list2是操做的它建立的獨立的列表(經過傳遞它本身的空列表做爲list``` 參數的值)。

extendList 函數的定義能夠作以下修改,但,當沒有新的 list 參數被指定的時候,會老是開始一個新列表,這更加多是一直指望的行爲。

def extendList(val, list=None):
    if list is None:
        list = []
    list.append(val)
    return list

使用這個改進的實現,輸出將是:

list1 = [10]
list2 = [123]
list3 = ['a']
相關文章
相關標籤/搜索