python是一門非常有趣的語言。它提供了許多非常方便的標準庫和許多內建命令是我們輕鬆完成任務.但是好東西太多了就有選擇恐懼症了,以至於我們不能很好第利用這個標準庫和它的基本機構。下面列出了一些對python新手來說很簡單有效的陷阱。
忽略了python的版本
這是一個在StackOverflow上不斷被人提起的問題。當你完美的程式碼跑在別人的電腦上就報錯是怎樣一種體驗,所以這個時候就需要檢查你們的python版本是否一致。確保程式碼跑在自己知道的python版本上。你可以通過以下程式碼檢視python版本:
1 2 |
$ python --version Python 2.7.9 |
python版本管理
pyenv是一個不錯的python版本管理工具。不幸的是它只執行在*nix系統上。在Mac OS上,你可以簡單用brew install pyenv
安裝,在linux系統中,有一個自動安裝器automatic installer
糾結於用一行程式碼解決所有問題
許多人誇口說我多牛用一行程式碼就解決了所有問題,即便他們的程式碼比正常寫的更缺少效率,而且這些程式碼也會更難以閱讀,甚至會出現歧義。比如說:
1 |
l = [m for a, b in zip(this, that) if b.method(a) != b for m in b if not m.method(a, b) and reduce(lambda x, y: a + y.method(), (m, a, b))] |
老實說上面的程式碼是我自己為了說明這件事情寫的。但是我可是真的見過有許多人這樣幹過。如果你只是簡單通過把東西新增到一個list或者一個set中來顯擺自己解決複雜問題的手段,那麼你有可能會得不償失。
一行程式碼控並不是什麼巨大的成就,儘管有時候看起來特別聰明。優秀的程式碼是簡潔但是更注重高效和易讀。
錯誤地初始化set
這是一個更加微妙的問題,有時候會讓你措手不及。set
推導式起來有點像list
推導式.
1 2 3 |
>>> { n for n in range(10) if n % 2 == 0 } {0, 8, 2, 4, 6} >>> type({ n for n in range(10) if n % 2 == 0 }) |
上面的例子說明了這點。set
有點像放在容器中的list
它們的區別是set
沒有重複的值和無序的。人們通常會把{}
認為是一個空的set
,可它不是,它是一個空的dict
.
1 2 3 |
>>> {} {} >>> type({}) |
所以如果我們想要初始化一個空的set
,就直接使用set()
1 2 3 |
>>> set() set() >>> type(set()) |
注意一個空的set
可以表示成set()
,但是一個包含了元素的集合要被定義成set([1, 2])
的樣子。
誤解了GIL
GIL(全域性直譯器鎖)意味著只有一個執行緒在一個Python程式可以執行在任何時間。 這意味著當我們不能建立一個執行緒,並期望它並行執行。 Python直譯器實際上做的是快速切換不同的執行執行緒。 但這是一個非常簡單的版本。 在許多例項中程式並行執行,像使用C擴充套件的庫時。 但Python程式碼執行時,大多數時候不會並行執行。換句話說,執行緒在Python中不像在Java或c++中一樣。
許多人會嘗試為Python辯解說,這些都是真正的執行緒。 3 這確實是真的,但並不能改變這樣一個事實:Python處理執行緒的方式不同於你期望的那樣。 Ruby也有類似的情況(還有一個直譯器鎖)。
規定的解決方案是使用multiprocessing
模組。multiprocessing
模組提供的過程類基本上可以很好地覆蓋分歧。 然而,分歧比執行緒代價高得多。所以並行執行不總是好的。
然而,這個問題不是每個Python程式都會遇到。PyPy-stm就是Python的一個實現不受GIL影響的例子。 實現建立在其他平臺上的像JVM(Jython) 或CLR(IronPython)沒有GIL的問題。
總之,在使用時要小心執行緒類,你得到的可能不是你想要的。
使用過時的樣式類
在Python 2有兩種型別的類,“舊式”類,“新風格”類。 如果 你使用Python 3,那麼你正在使用預設的“新風格”類。 為了確保你使用 “新風格”在Python 2類,您需要繼承object
或者任何你建立的不總是繼承內建指令int
或list
的新類 。 換句話說,你的基類,應該總是繼承object
。
1 2 |
class MyNewObject(object): # stuff here |
這些’新類’修復了一些非常基本的出現在老式類中問題,如果你感興趣可以檢視文件
錯誤的迭代
下面的這些錯誤對新手來說非常常見:
1 2 |
for name_index in range(len(names)): print(names[name_index]) |
很明顯沒有必要使用len, 實際上遍歷列表用非常簡單的語句就可以實現:
1 2 |
for name in names: print(name) |
此外,還有一大堆其他的工具在你處理簡化迭代。 例如,zip
可以用來遍歷兩個列表:
1 2 |
for cat, dog in zip(cats, dogs): print(cat, dog) |
如果我們要考慮索引和值列表變數,我們可以使用enumerate
1 2 |
for index, cat in enumerate(cats): print(cat, index) |
在itertools中還有很多功能可以選擇。如果itertools中有你想要的功能就很方便的拿來用。但是也不要過於為了用它而用它。
itertools濫用使得StackOverflow上的一個大神花了很多時間去解決它。
使用了可變的預設引數
我看過很多如下:
1 2 3 |
def foo(a, b, c=[]): # append to c # do some more stuff |
不要使用可變的預設引數,而不是使用以下:
1 2 3 4 5 |
def foo(a, b, c=None): if c is None: c = [] # append to c # do some more stuff |
下面這個例子可以很直觀地幫我們理解這個問題:
1 2 3 4 5 6 7 8 9 10 11 |
In[2]: def foo(a, b, c=[]): ... c.append(a) ... c.append(b) ... print(c) ... In[3]: foo(1, 1) [1, 1] In[4]: foo(1, 1) [1, 1, 1, 1] In[5]: foo(1, 1) [1, 1, 1, 1, 1, 1] |
相同的 c 被引用一次又一次的每一次呼叫該函式。 這可能會產生一些非常 不必要的後果。