前四個字是做WhiteBox時的評判,只有Programmer在意,因爲他們看得到内部的code,他們需要clean code是因爲這樣他們可以更快速的去理解(無論是自己寫的還是別人寫的),從而能在理解基礎上快速修復或增加新的東西。
后四個字是BlockBox時的評判,User和Developer都在意,這關乎的是一個成品的表現。成品在意的是效率,就是速度與精確。而一般來説,聰明的算法與數據結構總是有更好的效率。換句話說,我們現代CPU架構的模式使得在相同標準下短小精悍的code有優勢。
這些東西說太多,也只是意會,不好理解,下面用四個例子來做更好的作説明。需要注意的是,這四段Code都是合理優秀的,裏面就算最普通的第一個也是有如教科書般經典。
def f1(list):
string = ""
for item in list:
string = string + chr(item)
return string
def f2(list):
string = ""
ichr = chr
for item in list:
string = string + ichr(item)
return string
def f3(list):
return "".join([chr(item) for item in list])
def f4(list):
return "".join(map(chr, list))
給一個相對大的list,f1-f4的運行時間分別是:
- f1: 3.01409792928ms
- f2: 2.99986600876ms
- f3: 2.69300198555ms
- f4: 1.84089493752ms
- f1是最正規的寫法,只要不是很performance intensive的程序,相信大多數Programmer都是這麽寫。
- f2只比f1相比只有一處變化,性能上也只是稍微快了一點點。因爲python的特性,當執行chr()這個function的時候,intepretor需要不斷向上回溯去找chr()被定義的地方。而f2則將local variable ichr指向了chr,避免了每個loop都要向上回溯的時間。當然,這個減少的時間微乎其微,因爲大多數compiler都會幫你優化。
- f1和f2還有一個地方是很浪費資源的,就是string concatenation。一般Programming初學者總是喜歡用這個方法:初始化一個empty的string,然後再把内容用+ operator粘貼在後面。這個方法在string相對小並且操作量少的情況下並無壞處。但是在上面的function裏,每個loop可能都以運行十萬計的情況下,就非常耗費資源。因爲其操作是每次重新初始化一個string在memory裏,然後把舊的string和新的string添加在一起放入那個memory位置。這樣的complexity是O(n**2)。而使用join()這個内建函數,其complexity則是O(n),因爲它一次性把所有要加的部份粘在一起。
- 這4個function裏面,以f4的字符數最少,但是速度提升最大的也是f4。f4比f3的改變僅僅是用map代替了for,如此小的改變獲得的效果比前兩個改變大得多。你可以把map當作是for被移到了C code裏了,唯一不好的地方是map的loop body必須得是一個function。結論就是一個program的效率最大影響的還是compiler,只要有内置相同作用的function,還是用内置的更快也更方便。
以上是我最近從http://wiki.python.org/moin/PythonSpeed/PerformanceTips所學到的小技巧,每种語言都有其類似的提升方法。
Good programming style的視覺美感不僅僅是如何format及indent,簡潔的code也總是能有更高的性能。如果要做一個更好的Programmer,光遵循書上或者老師教的好code是不夠的,優雅的code,那是必需的。
No comments:
Post a Comment