問題1
到底什么是Python?你可以在回答中與其他技術進行對比(也鼓勵這樣做),
答案
下面是一些關鍵點:
- Python是一種解釋型語言,這就是說,與C語言和C的衍生語言不同,Python代碼在運行之前不需要編譯,其他解釋型語言還包括PHP和Ruby,
- Python是動態型別語言,指的是你在宣告變數時,不需要說明變數的型別,你可以直接撰寫類似x=111和x="I'm a string"這樣的代碼,程式不會報錯,
- Python非常適合面向物件的編程(OOP),因為它支持通過組合(composition)與繼承(inheritance)的方式定義類(class),Python中沒有訪問說明符(access specifier,類似C++中的public和private),這么設計的依據是“大家都是成年人了”,
- 在Python語言中,函式是第一類物件(first-class objects),這指的是它們可以被指定給變數,函式既能回傳函式型別,也可以接受函式作為輸入,類(class)也是第一類物件,
- Python代碼撰寫快,但是運行速度比編譯語言通常要慢,好在Python允許加入基于C語言撰寫的擴展,因此我們能夠優化代碼,消除瓶頸,這點通常是可以實作的,numpy就是一個很好地例子,它的運行速度真的非常快,因為很多算術運算其實并不是通過Python實作的,
- Python用途非常廣泛——網路應用,自動化,科學建模,大資料應用,等等,它也常被用作“膠水語言”,幫助其他語言和組件改善運行狀況,
- Python讓困難的事情變得容易,因此程式員可以專注于演算法和資料結構的設計,而不用處理底層的細節,
為什么提這個問題:
如果你應聘的是一個Python開發崗位,你就應該知道這是門什么樣的語言,以及它為什么這么酷,以及它哪里不好,
問題2
補充缺失的代碼
def print_directory_contents(sPath):
"""
這個函式接受檔案夾的名稱作為輸入引數,
回傳該檔案夾中檔案的路徑,
以及其包含檔案夾中檔案的路徑,
"""
# 補充代碼
答案
#Python學習交流群:778463939
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
下面代碼會輸出什么:
#Python學習交流群:778463939
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;如果我們不知道要往函式中傳入多少個關鍵詞引數,或者想傳入字典的值作為關鍵詞引數時,那就要使用**kwargs,args和kwargs這兩個識別符號是約定俗成的用法,你當然還可以用*bob和**billy,但是這樣就并不太妥,
下面是具體的示例:
#Python學習交流群:778463939
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),可以讓你以簡單易讀得方式裝飾目標物件,
#Python學習交流群:778463939
@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_1()會列印下面的結果:
<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),垃圾回收器會定時尋找這個回圈,并將其回收,舉個例子,假設有兩個物件o1和o2,而且符合
o1.x == o2和o2.x == o1這兩個條件,如果o1和o2沒有其他代碼參考,那么它們就不應該繼續存在,但它們的參考計數都是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
你有實施過個人專案嗎?
真的?
如果做過個人專案,這說明從更新自己的技能水平方面來看,你愿意比最低要求付出更多的努力,如果你有維護的個人專案,作業之外也堅持編碼,那么你的雇主就更可能把你視作為會增值的資產,即使他們不問這個問題,我也認為談談這個話題很有幫助,
結語
我給出的這些問題時,有意涉及了多個領域,而且答案也是特意寫的較為啰嗦,在編程面試中,你需要展示你對語言的理解,如果你能簡要地說清楚,那請務必那樣做,我盡量在答案中提供了足夠的資訊,即使是你之前從來沒有了解過這些領域,你也可以從答案中學到些東西,我希望本文能夠幫助你找到滿意的作業,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/171253.html
標籤:其他
