當前位置:
首頁 > 知識 > Python面試必須要看的15個問題

Python面試必須要看的15個問題


程序員大咖

點擊右側關注,免費進階高級!





來源:  EarlGrey@編程派   鏈接:

http://codingpy.com/article/essential-python-interview-questions/




引言



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




我之前還沒有出過和這些題目一樣難的面試題,如果你能輕鬆地回答出來的話,趕緊去找份工作吧!




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

特別要注意以下幾點:

  • 命名規範要統一。如果樣本代碼中能夠看出命名規範,遵循其已有的規範。

  • 遞歸函數需要遞歸併終止。確保你明白其中的原理,否則你將面臨無休無止的調用棧(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]

答案


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

]]

為什麼提這個問題

  • 列表解析(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、1、2和4。很棒吧。第三個函數調用的結果就有些奇怪了。它使用了之前內存地址中存儲的舊列表。這就是為什麼它的前兩個元素是0和1了。


不明白的話就試著運行下面的代碼吧:

l_mem = []

l = l_mem          

# the first call


for

i

in

range(

2

):
   l.append(i*i)

print

l            

# [0, 1]

l = [

3

,

2

,

1

]        

# the second call


for

i

in

range(

3

):
   l.append(i*i)

print

l            

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

l = l_mem          

# the third call


for

i

in

range(

3

):
   l.append(i*i)

print

l            

# [0, 1, 0, 1, 4]




問題7




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




答案


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


舉個例子:

import

datetime
datetime.datetime.now =

lambda

: datetime.datetime(

2012

,

12

,

12

)

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


為什麼提這個問題


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


另外:如果你沒讀過mock模塊的話,真的值得花時間讀一讀。這個模塊非常有用。


問題8




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




答案


如果我們不確定要往函數中傳入多少個參數,或者我們想往函數中以列表和元組的形式傳參數時,那就使要用*args;如果我們不知道要往函數中傳入多少個關鍵詞參數,或者想傳入字典的值作為關鍵詞參數時,那就要使用**kwargsargskwargs這兩個標識符是約定俗成的用法,你當然還可以用*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

)                    

# (1, 2, 3) {}


f(

1

,

2

,

3

,

"groovy"

)          

# (1, 2, 3, "groovy") {}


f(a=

1

,b=

2

,c=

3

)              

# () {"a": 1, "c": 3, "b": 2}


f(a=

1

,b=

2

,c=

3

,zzz=

"hi"

)    

# () {"a": 1, "c": 3, "b": 2, "zzz": "hi"}


f(

1

,

2

,

3

,a=

1

,b=

2

,c=

3

)        

# (1, 2, 3) {"a": 1, "c": 3, "b": 2}

f(*l,**d)                  

# (1, 2, 3) {"a": 7, "c": 9, "b": 8}


f(*t,**d)                  

# (4, 5, 6) {"a": 7, "c": 9, "b": 8}


f(

1

,

2

,*t)                  

# (1, 2, 4, 5, 6) {}


f(q=

"winning"

,**d)          

# () {"a": 7, "q": "winning", "c": 9, "b": 8}


f(

1

,

2

,*t,q=

"winning"

,**d)  

# (1, 2, 4, 5, 6) {"a": 7, "q": "winning", "c": 9, "b": 8}

def

f2

(arg1,arg2,*args,**kwargs)

:


   

print

arg1,arg2, args, kwargs

f2(

1

,

2

,

3

)                      

# 1 2 (3,) {}


f2(

1

,

2

,

3

,

"groovy"

)              

# 1 2 (3, "groovy") {}


f2(arg1=

1

,arg2=

2

,c=

3

)          

# 1 2 () {"c": 3}


f2(arg1=

1

,arg2=

2

,c=

3

,zzz=

"hi"

)  

# 1 2 () {"c": 3, "zzz": "hi"}


f2(

1

,

2

,

3

,a=

1

,b=

2

,c=

3

)          

# 1 2 (3,) {"a": 1, "c": 3, "b": 2}

f2(*l,**d)                  

# 1 2 (3,) {"a": 7, "c": 9, "b": 8}


f2(*t,**d)                  

# 4 5 (6,) {"a": 7, "c": 9, "b": 8}


f2(

1

,

2

,*t)                  

# 1 2 (4, 5, 6) {}


f2(

1

,

1

,q=

"winning"

,**d)      

# 1 1 () {"a": 7, "q": "winning", "c": 9, "b": 8}


f2(

1

,

2

,*t,q=

"winning"

,**d)  

# 1 2 (4, 5, 6) {"a": 7, "q": "winning", "c": 9, "b": 8}


為什麼提這個問題


有時候,我們需要往函數中傳入未知個數的參數或關鍵詞參數。有時候,我們也希望把參數或關鍵詞參數儲存起來,以備以後使用。有時候,僅僅是為了節省時間。




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

MyClass(object)

:


   

def

__init__

(self)

:


       self._some_property =

"properties are nice"


       self._some_other_property =

"VERY nice"


   

def

normal_method

(*args,**kwargs)

:


       

print

"calling normal_method({0},{1})"

.format(args,kwargs)
   

@classmethod


   

def

class_method

(*args,**kwargs)

:


       

print

"calling class_method({0},{1})"

.format(args,kwargs)
   

@staticmethod


   

def

static_method

(*args,**kwargs)

:


       

print

"calling static_method({0},{1})"

.format(args,kwargs)
   

@property


   

def

some_property

(self,*args,**kwargs)

:


       

print

"calling some_property getter({0},{1},{2})"

.format(self,args,kwargs)
       

return

self._some_property
   

@some_property.setter


   

def

some_property

(self,*args,**kwargs)

:


       

print

"calling some_property setter({0},{1},{2})"

.format(self,args,kwargs)
       self._some_property = args[

0

]
   

@property


   

def

some_other_property

(self,*args,**kwargs)

:


       

print

"calling some_other_property getter({0},{1},{2})"

.format(self,args,kwargs)
       

return

self._some_other_property

o = MyClass()

# 未裝飾的方法還是正常的行為方式,需要當前的類實例(self)作為第一個參數。

o.normal_method

# <bound method MyClass.normal_method of <__main__.MyClass instance at 0x7fdd2537ea28>>

o.normal_method()

# normal_method((<__main__.MyClass instance at 0x7fdd2537ea28>,),{})

o.normal_method(

1

,

2

,x=

3

,y=

4

)

# normal_method((<__main__.MyClass instance at 0x7fdd2537ea28>, 1, 2),{"y": 4, "x": 3})

# 類方法的第一個參數永遠是該類

o.class_method

# <bound method classobj.class_method of <class __main__.MyClass at 0x7fdd2536a390>>

o.class_method()

# class_method((<class __main__.MyClass at 0x7fdd2536a390>,),{})

o.class_method(

1

,

2

,x=

3

,y=

4

)

# class_method((<class __main__.MyClass at 0x7fdd2536a390>, 1, 2),{"y": 4, "x": 3})

# 靜態方法(static method)中除了你調用時傳入的參數以外,沒有其他的參數。

o.static_method

# <function static_method at 0x7fdd25375848>

o.static_method()

# static_method((),{})

o.static_method(

1

,

2

,x=

3

,y=

4

)

# static_method((1, 2),{"y": 4, "x": 3})

# @property是實現getter和setter方法的一種方式。直接調用它們是錯誤的。


# 「只讀」屬性可以通過只定義getter方法,不定義setter方法實現。

o.some_property

# 調用some_property的getter(<__main__.MyClass instance at 0x7fb2b70877e8>,(),{})


# "properties are nice"


# 「屬性」是很好的功能

o.some_property()

# calling some_property getter(<__main__.MyClass instance at 0x7fb2b70877e8>,(),{})


# Traceback (most recent call last):


#   File "<stdin>", line 1, in <module>


# TypeError: "str" object is not callable

o.some_other_property

# calling some_other_property getter(<__main__.MyClass instance at 0x7fb2b70877e8>,(),{})


# "VERY nice"

# o.some_other_property()


# calling some_other_property getter(<__main__.MyClass instance at 0x7fb2b70877e8>,(),{})


# Traceback (most recent call last):


#   File "<stdin>", line 1, in <module>


# TypeError: "str" object is not callable

o.some_property =

"groovy"


# calling some_property setter(<__main__.MyClass object at 0x7fb2b7077890>,("groovy",),{})

o.some_property

# calling some_property getter(<__main__.MyClass object at 0x7fb2b7077890>,(),{})


# "groovy"

o.some_other_property =

"very groovy"


# Traceback (most recent call last):


#   File "<stdin>", line 1, in <module>


# AttributeError: can"t set attribute

o.some_other_property

# calling some_other_property getter(<__main__.MyClass object at 0x7fb2b7077890>,(),{})




問題10




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

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()

答案


輸出結果以注釋的形式表示:

a.go()

# go A go!

b.go()

# go A go!


# go B go!

c.go()

# go A go!


# go C go!

d.go()

# go A go!


# go C go!


# go B go!


# go D go!

e.go()

# go A go!


# go C go!


# go B go!

a.stop()

# stop A stop!

b.stop()

# stop A stop!

c.stop()

# stop A stop!


# stop C stop!

d.stop()

# stop A stop!


# stop C stop!


# stop D stop!

e.stop()

# stop A stop!

a.pause()

# ... Exception: Not Implemented

b.pause()

# ... Exception: Not Implemented

c.pause()

# ... Exception: Not Implemented

d.pause()

# wait D wait!

e.pause()

# ...Exception: Not Implemented


為什麼提這個問題


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


問題11




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

class

Node(object)

:


   

def

__init__

(self,sName)

:


       self._lChildren = []
       self.sName = sName
   

def

__repr__

(self)

:


       

return

"<Node "{}">"

.format(self.sName)
   

def

append

(self,*args,**kwargs)

:


       self._lChildren.append(*args,**kwargs)
   

def

print_all_1

(self)

:


       

print

self
       

for

oChild

in

self._lChildren:
           oChild.print_all_1()
   

def

print_all_2

(self)

:


       

def

gen

(o)

:


           lAll = [o,]
           

while

lAll:
               oNext = lAll.pop(

0

)
               lAll.extend(oNext._lChildren)
               

yield

oNext
       

for

oNode

in

gen(self):
           

print

oNode

oRoot = Node(

"root"

)
oChild1 = Node(

"child1"

)
oChild2 = Node(

"child2"

)
oChild3 = Node(

"child3"

)
oChild4 = Node(

"child4"

)
oChild5 = Node(

"child5"

)
oChild6 = Node(

"child6"

)
oChild7 = Node(

"child7"

)
oChild8 = Node(

"child8"

)
oChild9 = Node(

"child9"

)
oChild10 = Node(

"child10"

)

oRoot.append(oChild1)
oRoot.append(oChild2)
oRoot.append(oChild3)
oChild1.append(oChild4)
oChild1.append(oChild5)
oChild2.append(oChild6)
oChild4.append(oChild7)
oChild3.append(oChild8)
oChild3.append(oChild9)
oChild6.append(oChild10)

# 說明下面代碼的輸出結果

oRoot.print_all_1()
oRoot.print_all_2()

答案


oRoot.print_all_1()會列印下面的結果:


<Node

"root"

>
<Node

"child1"

>
<Node

"child4"

>
<Node

"child7"

>
<Node

"child5"

>
<Node

"child2"

>
<Node

"child6"

>
<Node

"child10"

>
<Node

"child3"

>
<Node

"child8"

>
<Node

"child9"

>



oRoot.print_all_2()

會列印下面的結果:


<Node

"root"

>
<Node

"child1"

>
<Node

"child2"

>
<Node

"child3"

>
<Node

"child4"

>
<Node

"child5"

>
<Node

"child6"

>
<Node

"child8"

>
<Node

"child9"

>
<Node

"child7"

>
<Node

"child10"

>


為什麼提這個問題


因為對象的精髓就在於組合(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)。垃圾回收器會定時尋找這個循環,並將其回收。舉個例子,假設有兩個對象o1o2,而且符合o1.x == o2o2.x == o1這兩個條件。如果o1o2沒有其他代碼引用,那麼它們就不應該繼續存在。但它們的引用計數都是1。



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




問題13




將下面的函數按照執行效率高低排序。它們都接受由0至1之間的數字構成的列表作為輸入。這個列表可以很長。一個輸入列表的示例如下:[random.random() for i in range(100000)]。你如何證明自己的答案是正確的。

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

)]

答案


按執行效率從高到低排列:f2、f1和f3。要證明這個

答案

是對的,你應該知道如何分析自己代碼的性能。Python中有一個很好的程序分析包,可以滿足這個需求。

import

cProfile
lIn = [random.random()

for

i

in

range(

100000

)]
cProfile.run(

"f1(lIn)"

)
cProfile.run(

"f2(lIn)"

)
cProfile.run(

"f3(lIn)"

)

為了向大家進行完整地說明,下面我們給出上述分析代碼的輸出結果:



>>>

cProfile.run(

"f1(lIn)"

)
       

4

function calls

in

0.045

seconds

  Ordered by: standard name

  ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       

1

   

0.009

   

0.009

   

0.044

   

0.044

<stdin>:

1

(f1)
       

1

   

0.001

   

0.001

   

0.045

   

0.045

<string>:

1

(<module>)
       

1

   

0.000

   

0.000

   

0.000

   

0.000

{method

"disable"

of

"_lsprof.Profiler"

objects}
       

1

   

0.035

   

0.035

   

0.035

   

0.035

{sorted}

>>>

cProfile.run(

"f2(lIn)"

)
       

4

function calls

in

0.024

seconds

  Ordered by: standard name

  ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       

1

   

0.008

   

0.008

   

0.023

   

0.023

<stdin>:

1

(f2)
       

1

   

0.001

   

0.001

   

0.024

   

0.024

<string>:

1

(<module>)
       

1

   

0.000

   

0.000

   

0.000

   

0.000

{method

"disable"

of

"_lsprof.Profiler"

objects}
       

1

   

0.016

   

0.016

   

0.016

   

0.016

{sorted}

>>>

cProfile.run(

"f3(lIn)"

)
       

4

function calls

in

0.055

seconds

  Ordered by: standard name

  ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       

1

   

0.016

   

0.016

   

0.054

   

0.054

<stdin>:

1

(f3)
       

1

   

0.001

   

0.001

   

0.055

   

0.055

<string>:

1

(<module>)
       

1

   

0.000

   

0.000

   

0.000

   

0.000

{method

"disable"

of

"_lsprof.Profiler"

objects}
       

1

   

0.038

   

0.038

   

0.038

   

0.038

{sorted}



為什麼提這個問題


定位並避免代碼瓶頸是非常有價值的技能。想要編寫許多高效的代碼,最終都要回答常識上來——在上面的例子中,如果列表較小的話,很明顯是先進行排序更快,因此如果你可以在排序前先進行篩選,那通常都是比較好的做法。其他不顯而易見的問題仍然可以通過恰當的工具來定位。因此了解這些工具是有好處的。


問題14




你有過失敗的經歷嗎?




錯誤的答案


我從來沒有失敗過!


為什麼提這個問題


恰當地回答這個問題說明你用於承認錯誤,為自己的錯誤負責,並且能夠從錯誤中學習。如果你想變得對別人有幫助的話,所有這些都是特別重要的。如果你真的是個完人,那就太糟了,回答這個問題的時候你可能都有點創意了。




問題15




你有實施過個人項目嗎?




真的?


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


結語





我給出的這些問題時,有意涉及了多個領域。而且答案也是特意寫的較為啰嗦。在編程面試中,你需要展示自己對語言的理解,如果你能簡要地說清楚,那請務必那樣做。我盡量在答案中提供了足夠的信息,即使是你之前從來沒有了解過這些領域,你也可以從答案中學到些東西。我希望本文能夠幫助你找到滿意的工作。




【點擊成為源碼大神】

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python開發 的精彩文章:

Python分散式爬蟲詳解(一)
趁夜深人靜

TAG:Python開發 |