關于Python的一些吐槽
Python是一門挺好的語言,但用著久了,也還是想吐槽一下:)
不夠方便的文檔
一直都覺得Python的文檔不夠方便。自己在使用上遇到的一個主要問題是對象類型查看不便。舉個簡單的例子,sys模塊中定義的_current_frames方法,在文檔中描述了其功能,
sys._current_frames() Return a dictionary mapping each thread’s identifier to the topmost stack frame currently active in that thread at the time the function is called. Note that functions in the traceback module can build the call stack given such a frame. ...
然而這里返回的frame對象的定義又在哪呢?沒有辦法直接從這里點擊跳轉。那frame的定義在何處呢?在inspect里面可以找到,但即便在這個頁面內也沒有頁面內的跳轉。不知道其他人是怎么解決這個問題的,自己很多時候都是去shell下help一下看看,有夠原始的。
不一致的命名
Python標準庫中的模塊存在好幾種不同命名方式,比如,
StringIO cStringIO copy_reg linecache
第一次見到的時候對這個事情是非常困惑的,因為這種不一致的命名方式增加了記憶的復雜度。在標準庫都使用了各種不一致命名的情況下,各種第三方庫則是選擇了各自偏好的命名方式,于是實際項目中這種不一致的命名就會泛濫開來。
除了命名方式之外,還有一個略困惑的事情就是,urllib、urllib2。不知道當時的設計者是怎么考慮的,竟然在標準庫里面出現了這種模塊,于是也難怪出現了第三方的urllib3。一言不合就實現新版本,然后名稱+1..
不符合預期的函數默認參數
Python函數默認參數中如果使用了mutable對象,那么這個參數是共享的,
def foobar(nums=[]): nums.append(1) print sum(nums) >>> foobar() 1 >>> foobar() 2 >>> foobar() 3
這是一個容易產生bug的語言特性,曾經也踩過這個坑。StackOverflow也有關于此的討論。不少Python Best Practice會提醒我們避開這種使用方式,但如果一開始就符合預期,應該會更好。
不符合直覺的循環中的lambda定義
同樣也是一個容易產生bug的特性,
funcs = [] for x in xrange(5): funcs.append(lambda: x + 1) for func in funcs: print func()
上述代碼的輸出全都是5,lambda中的x被綁定至了最后一次出現的值。因為這種不當使用導致的bug,估計不少人都遇到過。
記不清的__getattr__
, __getattribute__
這兩個函數其中一個是每次屬性訪問的時候都會被調用、另一個是當對象上沒有該屬性時被調用。功能很容易理解,但記憶上卻很容易混淆,每次記清楚之后一段時間未用,就又搞不清了,只能運行遍代碼試試。
原本attr就是attribute的縮寫,于是記憶混淆就難免了。Python郵件組里曾經也有過關于這個的討論。
容易被忽略的','
不止一次遇到過因為賦值語句末尾的逗號導致的類型異常,這確實是很蠢的一個錯誤,但偏偏不太容易注意到,
foo = 'foo',
上述語句末尾的','會導致foo變成一個tuple,而不是str。有時候真的是不小心或是無意識按到了',',然后運行時就悲劇了。另一方面Python的tuple定義也是有點小混淆的,
t = tuple() # create tuple t = () # create tuple t = (1,) # create tuple t = 1, # create tuple t = (()) # create tuple t = (1) # just integer t = (x for x in xrange(5)) # create generator
()這個操作符在不同使用方式中語義是不同的,是不是有點煩人呢?
可以動態添加屬性的函數與不能動態添加屬性的容器類(list, dict, set...)
在命令行下執行下列代碼,
# no error def foobar(): pass foobar.name = 'foobar' # exception rased, AttributeError: 'dict' object has no attribute 'name' foobar = {} foobar.name = 'foobar'
很奇怪為什么Python中的函數可以直接進行類似的動態設置屬性操作,而常見的容器類們都不可以。函數屬性這一條還是PEP232中通過的。StackOverflow也有不錯的討論。
這個特性的功能不難理解,但為啥別的一些類型不可以。只是單純的對一致性產生了懷疑。
可以被修改的True、False
試試下面這小段代碼,
True, False = False, True if 1 > 0 is True: print 'correct' else: print 'wrong'
這個問題在Python 3中得到了解決,True/False/None已經變成了保留字,但在2.x中還是依然如此,希望沒人在正式代碼中來這么一下。
不被推薦使用的for..else, while..else
為什么Python中加入了for..else、while..else,StackOverflow上這個問題有不少有價值的討論。曾經嘗試過在代碼中使用,但一段時間之后自己就忘記了這個else到底是什么時候會進入觸發。
這兩個else所期望實現的功能是很明確的,但這個功能卻用了else來進行標識,這與if..else中的else起始含義是不一樣的,同樣的名字不一樣的含義,自然就是不好記憶的。
''、""、''''''、""""""
Python字符串定義可以使用這幾種符號,
foobar = 'foobar' foobar = "foobar" foobar = '''foobar''' foobar = """foobar""" foobar = 'foo"bar' foobar = "foo'bar" foobar = """foo'''bar""" foobar = '''foo"""bar'''
對單引號、雙引號都支持,個人理解是為了方便。這兩組對應的使用方式是等價的,但PEP257中竟然說為了保持一致docstring推薦使用"""""",這又是為什么?真有保持一致的需要,就不應該對單引號、雙引號都支持。否則真是平添了StackOverflow上的這些討論。
空格與Tab
Python的縮進像大多數語言那樣支持空格與Tab,但Python與常用語言的差異也在于Python中的縮進是有意義的,不匹配的縮進會導致語法錯誤。在代碼中混用空格、Tab是一件危險的行為。于是不同的項目可能約定不同的縮進方式,于是也有了網上很多無謂的討論。
如果Python的縮進不會導致語法錯誤,那么不對此進行約定是很正常的。可偏偏縮進就有可能導致錯誤,這種時候語言應該對此進行強制限定才是。
其它
一些眾所周知的問題這里就沒有必要再記錄了。Python最大的問題可能還是Python2、Python3的分裂。可惜沒有機會在正式項目中使用Python3,不然倒也想實際對比下兩者的差異與改進。
最后還是來看下The Zen of Python,
import python
The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those!
與其說是The Zen of Python不如說是The Zen of Programming,可惜其中一些條目是很難一如既往的保持的。
比如,
Special cases aren't special enough to break the rules.
不少語言特性的引入就是為了解決一些并不常見的情況。但解決這些問題真的有必要直接增加語言特性嗎?
再比如,
In the face of ambiguity, refuse the temptation to guess.
混淆的特性、混淆的縮寫命名..
再再比如,
There should be one-- and preferably only one --obvious way to do it.
這一條感覺太太難以實現了。看看網上的各種討論就知道,這一條大部分時候也只是停留在紙面上。
總結
寫了那么多,主要還是希望不論Python還是自己實現的代碼盡量符合這里定義的“Zen”。一些問題估計也是歷史遺留問題,為了保持兼容,不得不保持現狀。
另外也可以發現即便Python有一些缺點,但它無疑是一門成功的語言,而且依然還處在發展期。這足以說明在現實中,是否能夠獲得的成功,不在于缺點如何,而在于優點如何。讓優點足夠優秀,那么就有可能從競爭中勝出。