注:本文的原文是 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 1
。ide
最後,若是該值在父類中被改變(例如,咱們執行語句 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
沒有被指定的列表參數調用的時候,其使用的是同一個列表。這就是爲何當函數被定義的時候,表達式是用默認參數被計算,而不是它被調用的時候。
所以,list1
和 list3
是操做的相同的列表。而 ````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']