59個(gè)Python使用技巧,從此你的Python與眾不同!

今天給大家分享幾個(gè)Python使用的小技巧,原文來(lái)自于Python 技巧總結(jié),進(jìn)行了細(xì)微的調(diào)整,感謝作者!

1. 枚舉 - enumerate 可以有參數(shù)哦

之前我們這樣操作:

i = 0
for item in iterable:
print i, item
 i += 1

現(xiàn)在我們這樣操作:

for i, item in enumerate(iterable):

 print i, item

enumerate函數(shù)還可以接收第二個(gè)參數(shù)。就像下面這樣:

>>> list(enumerate('abc'))
[(0, 'a'), (1, 'b'), (2, 'c')] 
>>> list(enumerate('abc', 1)) 
[(1, 'a'), (2, 'b'), (3, 'c')]
```
#2. 字典/集合 解析
你也許知道如何進(jìn)行列表解析,但是可能不知道字典/集合解析。它們簡(jiǎn)單易用且高效。就像下面這個(gè)例子:
```
my_dict = {i: i * i for i in xrange(100)} 
my_set = {i * 15 for i in xrange(100)}
# There is only a difference of ':' in both
# 兩者的區(qū)別在于字典推導(dǎo)中有冒號(hào)
```
#3. 強(qiáng)制浮點(diǎn)除法
```
from __future__ import division 
result = 1/2
# print(result)
# 0.5
```
#4. 對(duì)Python表達(dá)式求值
我們都知道eval函數(shù),但是我們知道literal_eval函數(shù)么?也許很多人都不知道吧。可以用這種操作:
```
import ast 
my_list = ast.literal_eval(expr) 
```
來(lái)代替以下這種操作:
```
expr = "[1, 2, 3]"
my_list = eval(expr)
```
我相信對(duì)于大多數(shù)人來(lái)說(shuō)這種形式是第一次看見(jiàn),但是實(shí)際上這個(gè)在Python中已經(jīng)存在很長(zhǎng)時(shí)間了。
#5. 字符串/數(shù)列 逆序
你可以用以下方法快速逆序排列數(shù)列:
```
>>> a = [1,2,3,4]
>>> a[::-1]
[4, 3, 2, 1]

# This creates a new reversed list. 
# If you want to reverse a list in place you can do:
 
a.reverse()
```
這總方式也同樣適用于字符串的逆序:
```
>>> foo = "yasoob"
>>> foo[::-1]
'boosay'
```
#6. 三元運(yùn)算
三元運(yùn)算是if-else 語(yǔ)句的快捷操作,也被稱為條件運(yùn)算。這里有幾個(gè)例子可以供你參考,它們可以讓你的代碼更加緊湊,更加美觀。
```
[on_true] if [expression] else [on_false]
x, y = 50, 25
small = x if x < y else y
```
#7. Python里面如何拷貝一個(gè)對(duì)象
標(biāo)準(zhǔn)庫(kù)中的copy模塊提供了兩個(gè)方法來(lái)實(shí)現(xiàn)拷貝.一個(gè)方法是copy,它返回和參數(shù)包含內(nèi)容一樣的對(duì)象.
```
import copy
new_list = copy.copy(existing_list)
```
有些時(shí)候,你希望對(duì)象中的屬性也被復(fù)制,可以使用deepcopy方法:
```
import copy
new_list_of_dicts = copy.deepcopy(existing_list_of_dicts)
copy(x)
Shallow copy operation on arbitrary Python objects.
deepcopy(x, memo=None, _nil=[])
Deep copy operation on arbitrary Python objects.
```
#8. python中如何判斷對(duì)象相等
```
首先是C#中字符串的==和equal方法。
“==” :
對(duì)于內(nèi)置值類型而言, == 判斷兩個(gè)內(nèi)存值是否相等。
對(duì)于用戶自定義的值類型而言(Struct), == 需要重載,否則不能使用。
對(duì)于引用類型而言,默認(rèn)是同一引用才返回true,但是系統(tǒng)重載了很多引用類型的 == (比如下文提到的string),所以c#中引用類型的比較并不建議使用 ==。
“equals” :
對(duì)于值類型而言, 內(nèi)存相等才返回true。
對(duì)于引用類型而言,指向同一個(gè)引用才算相等。
但是比較特殊的是字符串String,是一個(gè)特殊的引用型類型,在C#語(yǔ)言中,重載了string的equals()方法,使string對(duì)象用起來(lái)就像是值類型一樣。
python中的 ==
python中的對(duì)象包含三要素:id, type, value
id 用來(lái)標(biāo)識(shí)唯一一個(gè)對(duì)象,type標(biāo)識(shí)對(duì)象的類型,value用來(lái)設(shè)置對(duì)象的值。
is 判斷是否是一個(gè)對(duì)象,使用id來(lái)判斷的。
== 是判斷a對(duì)象的值是否是b對(duì)象的值,默認(rèn)調(diào)用它的__eq__方法。
```
#9. 命名技巧
今天閱讀代碼,發(fā)現(xiàn)一個(gè)不錯(cuò)的函數(shù)命名方式:
```
def request(_argv): 
```
就是把所有的參數(shù)前面都加上_下劃線,這樣你在函數(shù)體中,一眼就可以看出那些是局部變量,那些是作為參數(shù)傳入的,類似把全局變量前面加上g。
#10. 開(kāi)發(fā)者工具集錦
+ pydoc: 模塊可以根據(jù)源代碼中的docstrings為任何可導(dǎo)入模塊生成格式良好的文檔。
+ doctest模塊:該模塊可以從源代碼或獨(dú)立文件的例子中抽取出測(cè)試用例。
+ unittest模塊:該模塊是一個(gè)全功能的自動(dòng)化測(cè)試框架,該框架提供了對(duì)測(cè)試準(zhǔn)備(test fixtures), 預(yù)定義測(cè)試集(predefined test suite)以及測(cè)試發(fā)現(xiàn)(test discovery)的支持。
+ trace:模塊可以監(jiān)控Python執(zhí)行程序的方式,同時(shí)生成一個(gè)報(bào)表來(lái)顯示程序的每一行執(zhí)行的次數(shù)。這些信息可以用來(lái)發(fā)現(xiàn)未被自動(dòng)化測(cè)試集所覆蓋的程序執(zhí)行路徑,也可以用來(lái)研究程序調(diào)用圖,進(jìn)而發(fā)現(xiàn)模塊之間的依賴關(guān)系。編寫并執(zhí)行測(cè)試可以發(fā)現(xiàn)絕大多數(shù)程序中的問(wèn)題,Python使得debug工作變得更加簡(jiǎn)單,這是因?yàn)樵诖蟛糠智闆r下,Python都能夠?qū)⑽幢惶幚淼腻e(cuò)誤打印到控制臺(tái)中,我們稱這些錯(cuò)誤信息為traceback。如果程序不是在文本控制臺(tái)中運(yùn)行的,traceback也能夠?qū)㈠e(cuò)誤信息輸出到日志文件或是消息對(duì)話框中。當(dāng)標(biāo)準(zhǔn)的traceback無(wú)法提供足夠的信息時(shí),可以使用cgitb 模塊來(lái)查看各級(jí)棧和源代碼上下文中的詳細(xì)信息,比如局部變量。cgitb模塊還能夠?qū)⑦@些跟蹤信息以HTML的形式輸出,用來(lái)報(bào)告web應(yīng)用中的錯(cuò)誤。
+ pdb:該模塊可以顯示出程序在錯(cuò)誤產(chǎn)生時(shí)的執(zhí)行路徑,同時(shí)可以動(dòng)態(tài)地調(diào)整對(duì)象和代碼進(jìn)行調(diào)試。
+ profile, timeit: 開(kāi)發(fā)者可以使用profile以及timit模塊來(lái)測(cè)試程序的速度,找出程序中到底是哪里很慢,進(jìn)而對(duì)這部分代碼獨(dú)立出來(lái)進(jìn)行調(diào)優(yōu)的工作。
+ compileall: Python程序是通過(guò)解釋器執(zhí)行的,解釋器的輸入是原有程序的字節(jié)碼編譯版本。這個(gè)字節(jié)碼編譯版本可以在程序執(zhí)行時(shí)動(dòng)態(tài)地生成,也可以在程序打包的時(shí)候就生成。compileall模塊可以處理程序打包的事宜,它暴露出了打包相關(guān)的接口,該接口能夠被安裝程序和打包工具用來(lái)生成包含模塊字節(jié)碼的文件。同時(shí),在開(kāi)發(fā)環(huán)境中,compileall模塊也可以用來(lái)驗(yàn)證源文件是否包含了語(yǔ)法錯(cuò)誤。
+ YAPF:Google開(kāi)源的Python代碼格式化工具。
+ iPDB: iPDB是一個(gè)極好的工具,我已經(jīng)用它查出了很多匪夷所思的bug。pip install ipdb 安裝該工具,然后在你的代碼中import ipdb; ipdb.set_trace(),然后你會(huì)在你的程序運(yùn)行時(shí),獲得一個(gè)很好的交互式提示。它每次執(zhí)行程序的一行并且檢查變量。
+ pycallgraph: 在一些場(chǎng)合,我使用pycallgraph來(lái)追蹤性能問(wèn)題。它可以創(chuàng)建函數(shù)調(diào)用時(shí)間和次數(shù)的圖表。
+ objgraph: objgraph對(duì)于查找內(nèi)存泄露非常有用。

![pycallgraph.jpg](http://upload-images.jianshu.io/upload_images/2007846-53baf267400a383f.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
#11. Python代碼微優(yōu)化之加快查找
```
collections.OrderedDict類:
def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
    if key not in self:
        root = self.__root
        last = root[0]
        last[1] = root[0] = self.__map[key] = [last, root, key]
    return dict_setitem(self, key, value)
```
注意最后一個(gè)參數(shù):dict_setitem=dict.**setitem**。如果你仔細(xì)想就會(huì)感覺(jué)有道理。將值關(guān)聯(lián)到鍵上,你只需要給__setitem__傳遞三個(gè)參數(shù):要設(shè)置的鍵,與鍵關(guān)聯(lián)的值,傳遞給內(nèi)建dict類的__setitem__類方法。等會(huì),好吧,也許最后一個(gè)參數(shù)沒(méi)什么意義。 最后一個(gè)參數(shù)其實(shí)是將一個(gè)函數(shù)綁定到局部作用域中的一個(gè)函數(shù)上。具體是通過(guò)將dict.__setitem__賦值為參數(shù)的默認(rèn)值。這里還有另一個(gè)例子:
```
def not_list_or_dict(value):
  return not (isinstance(value, dict) or isinstance(value, list))
def not_list_or_dict(value, _isinstance=isinstance, _dict=dict, _list=list):
  return not (_isinstance(value, _dict) or _isinstance(value, _list))
```
這里我們做同樣的事情,把本來(lái)將會(huì)在內(nèi)建命名空間中的對(duì)象綁定到局部作用域中去。因此,python將會(huì)使用LOCAL_FAST而不是LOAD_GLOBAL(全局查找)。那么這到底有多快呢?我們做個(gè)簡(jiǎn)單的測(cè)試:
```
$ python -m timeit -s 'def not_list_or_dict(value): return not (isinstance(value, dict) or isinstance(value, list))' 'not_list_or_dict(50)'

1000000 loops, best of 3: 0.48 usec per loop

$ python -m timeit -s 'def not_list_or_dict(value, _isinstance=isinstance, _dict=dict, _list=list): return not (_isinstance(value, _dict) or _isinstance(value, _list))' 'not_list_or_dict(50)'

1000000 loops, best of 3: 0.423 usec per loop
```
換句話說(shuō),大概有11.9%的提升 [2]。比我在文章開(kāi)始處承諾的5%還多!
#12. 包管理
Python世界最棒的地方之一,就是大量的第三方程序包。同樣,管理這些包也非常容易。按照慣例,會(huì)在 requirements.txt 文件中列出項(xiàng)目所需要的包。每個(gè)包占一行,通常還包含版本號(hào)。
```
pelican==3.3

Markdown

pelican-extended-sitemap==1.0.0
```
#13. Python函數(shù)參數(shù)默認(rèn)值的陷阱和原理深究
```
Python 2.7.9 (default, Dec 19 2014, 06:05:48)

[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)] on darwin

Type "help", "copyright", "credits" or "license" for more information.

>>> def generate_new_list_with(my_list=[], element=None):

... my_list.append(element)

... return my_list

...

>>> list_1 = generate_new_list_with(element=1)

>>> list_1

[1]

>>> list_2 = generate_new_list_with(element=2)

>>> list_2

[1, 2]

>>>
```
可見(jiàn)代碼運(yùn)行結(jié)果并不和我們預(yù)期的一樣。list_2在函數(shù)的第二次調(diào)用時(shí)并沒(méi)有得到一個(gè)新的list并填入2,而是在第一次調(diào)用結(jié)果的基礎(chǔ)上append了一個(gè)2。為什么會(huì)發(fā)生這樣在其他編程語(yǔ)言中簡(jiǎn)直就是設(shè)計(jì)bug一樣的問(wèn)題呢? 可見(jiàn)如果參數(shù)默認(rèn)值是在函數(shù)編譯compile階段就已經(jīng)被確定。之后所有的函數(shù)調(diào)用時(shí),如果參數(shù)不顯示的給予賦值,那么所謂的參數(shù)默認(rèn)值不過(guò)是一個(gè)指向那個(gè)在compile階段就已經(jīng)存在的對(duì)象的指針。如果調(diào)用函數(shù)時(shí),沒(méi)有顯示指定傳入?yún)?shù)值得話。那么所有這種情況下的該參數(shù)都會(huì)作為編譯時(shí)創(chuàng)建的那個(gè)對(duì)象的一種別名存在。如果參數(shù)的默認(rèn)值是一個(gè)不可變(Imuttable)數(shù)值,那么在函數(shù)體內(nèi)如果修改了該參數(shù),那么參數(shù)就會(huì)重新指向另一個(gè)新的不可變值。而如果參數(shù)默認(rèn)值是和本文最開(kāi)始的舉例一樣,是一個(gè)可變對(duì)象(Muttable),那么情況就比較糟糕了。所有函數(shù)體內(nèi)對(duì)于該參數(shù)的修改,實(shí)際上都是對(duì)compile階段就已經(jīng)確定的那個(gè)對(duì)象的修改。
#14. 單下劃線(_)
1、在解釋器中:在這種情況下,“_”代表交互式解釋器會(huì)話中上一條執(zhí)行的語(yǔ)句的結(jié)果。這種用法首先被標(biāo)準(zhǔn)CPython解釋器采用,然后其他類型的解釋器也先后采用。
```
>>> _ Traceback (most recent call last): 

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

NameError: name '_' is not defined 

>>> 42

>>> _ 

42

>>> 'alright!' if _ else ':('

'alright!'

>>> _ 

'alright!'
```
2、作為一個(gè)名稱:這與上面一點(diǎn)稍微有些聯(lián)系,此時(shí)“*”作為臨時(shí)性的名稱使用。這樣,當(dāng)其他人閱讀你的代碼時(shí)將會(huì)知道,你分配了一個(gè)特定的名稱,但是并不會(huì)在后面再次用到該名稱。例如,下面的例子中,你可能對(duì)循環(huán)計(jì)數(shù)中的實(shí)際值并不感興趣,此時(shí)就可以使用“*”。
```
n = 42

for _ in range(n): 

    do_something()
```
3、國(guó)際化:也許你也曾看到”_“會(huì)被作為一個(gè)函數(shù)來(lái)使用。這種情況下,它通常用于實(shí)現(xiàn)國(guó)際化和本地化字符串之間翻譯查找的函數(shù)名稱,這似乎源自并遵循相應(yīng)的C約定。例如,在Django文檔“轉(zhuǎn)換”章節(jié)中,你將能看到如下代碼:
```
from django.utils.translation import ugettext as _ 

from django.http import HttpResponse 

def my_view(request): 

    output = _("Welcome to my site.") 

    return HttpResponse(output)
```
可以發(fā)現(xiàn),場(chǎng)景二和場(chǎng)景三中的使用方法可能會(huì)相互沖突,所以我們需要避免在使用“*”作為國(guó)際化查找轉(zhuǎn)換功能的代碼塊中同時(shí)使用“*”作為臨時(shí)名稱。
#15. 名稱前的單下劃線(如:_shahriar)
程序員使用名稱前的單下劃線,用于指定該名稱屬性為“私有”。這有點(diǎn)類似于慣例,為了使其他人(或你自己)使用這些代碼時(shí)將會(huì)知道以“_”開(kāi)頭的名稱只供內(nèi)部使用。正如Python文檔中所述:
以下劃線 __ 為前綴的名稱(如_pam)應(yīng)該被視為API中非公開(kāi)的部分(不管是函數(shù)、方法還是數(shù)據(jù)成員)。此時(shí),應(yīng)該將它們看作是一種實(shí)現(xiàn)細(xì)節(jié),在修改它們時(shí)無(wú)需對(duì)外部通知。
正如上面所說(shuō),這確實(shí)類似一種慣例,因?yàn)樗鼘?duì)解釋器來(lái)說(shuō)確實(shí)有一定的意義,如果你寫了代碼 : from <模塊/包名> import *
 ,那么以 _ 開(kāi)頭的名稱都不會(huì)被導(dǎo)入,除非模塊或包中的 __all__
 列表顯式地包含了它們。了解更多請(qǐng)查看 Importing * in Python

#16. 名稱前的雙下劃線(如:__shahriar)
名稱(具體為一個(gè)方法名)前雙下劃線 _ 的用法并不是一種慣例,對(duì)解釋器來(lái)說(shuō)它有特定的意義。Python中的這種用法是為了避免與子類定義的名稱沖突。Python文檔指出,__spam 這種形式(至少兩個(gè)前導(dǎo)下劃線,最多一個(gè)后續(xù)下劃線)的任何標(biāo)識(shí)符將會(huì)被 正如所預(yù)料的,“_internal_use”并未改變,而“__method_name”卻被變成了“_ClassName__method_name”。此時(shí),如果你創(chuàng)建A的一個(gè)子類B,那么你將不能輕易地覆寫A中的方法“__method_name”。spam 這種形式原文取代,在這里 classname 是去掉前導(dǎo)下劃線的當(dāng)前類名。例如下面的例子:
```
>>> class A(object): 

... def _internal_use(self): 

... pass

... def __method_name(self): 

... pass

... 

>>> dir(A()) 

['_A__method_name', ..., '_internal_use']
```
正如所預(yù)料的,“_internal_use”并未改變,而“__method_name”卻被變成了“_ClassName__method_name”。此時(shí),如果你創(chuàng)建A的一個(gè)子類B,那么你將不能輕易地覆寫A中的方法“__method_name”。
#17. 名稱前后的雙下劃線(如:**init**)
這種用法表示Python中特殊的方法名。其實(shí),這只是一種慣例,對(duì)Python系統(tǒng)來(lái)說(shuō),這將確保不會(huì)與用戶自定義的名稱沖突。通常,你將會(huì)覆寫這些方法,并在里面實(shí)現(xiàn)你所需要的功能,以便Python調(diào)用它們。例如,當(dāng)定義一個(gè)類時(shí),你經(jīng)常會(huì)覆寫“**init**”方法。
雖然你也可以編寫自己的特殊方法名,但不要這樣做。
#17. 隱藏特性 1,函數(shù)unpack
```
def foo(x, y):

 print x, y

alist = [1, 2]

adict = {'x': 1, 'y': 2}

foo(*alist) # 1, 2

foo(**adict) # 1, 2
```
#18. 隱藏特性 2, 鏈?zhǔn)奖容^操作符
```
>>> x = 3

>>> 1 < x < 5

True

>>> 4 > x >=3

True
```
#19. 隱藏特性 3,函數(shù)的默認(rèn)參數(shù)
```
>>> def foo(x=[]):

... x.append(1)

... print x

...

>>> foo()

[1]

>>> foo()

[1, 1]
```
更安全的做法是:
```
>>> def foo(x=None):

... if x is None:

... x = []

... x.append(1)

... print x

...

>>> foo()

[1]

>>> foo()

[1]

>>>
```
#20. 隱藏特性 4,字典的get方法
#21. 隱藏特性 5,帶關(guān)鍵字的格式化
```
>>> print "Hello %(name)s !" % {'name': 'James'}

Hello James !

>>> print "I am years %(age)i years old" % {'age': 18}

I am years 18 years old
```
更新些的格式化:
```
>>> print "Hello {name} !".format(name="James")

Hello James !
```
#22. 隱藏特性 6,切片操作的步長(zhǎng)參數(shù)
```
可以用步長(zhǎng) -1 來(lái)反轉(zhuǎn)鏈表:
>>> a = [1, 2, 3, 4, 5]

>>> a[::2]

[1, 3, 5]

>>> a[::-1]

[5, 4, 3, 2, 1]

>>>
```
#23. 隱藏特性 7,嵌套列表推導(dǎo)式
```
[(i, j) for i in range(3) for j in range(i)]

[(1, 0), (2, 0), (2, 1)]
```
列表推導(dǎo)構(gòu)造permutation:可以用 itertools.permutations 來(lái)實(shí)現(xiàn)。
```
In[47]: a = 'abcd'

In[48]: [i+j+k for i in a for j in a.replace(i,'') for k in a.replace(i,'').replace(j,'')]

Out[48]: 

['abc',

 'abd',

 'acb',

 'acd',

 'adb',

 'adc',

 'bac',

 'bad',

 'bca',

 'bcd',

 'bda',

 'bdc',

 'cab',

 'cad',

 'cba',

 'cbd',

 'cda',

 'cdb',

 'dab',

 'dac',

 'dba',

 'dbc',

 'dca',

 'dcb']
```
#24. 隱藏特性 8,print 重定向輸出到文件
注意打開(kāi)的模式: “w+” 而不能 “w” , 當(dāng)然 “a” 是可以的
```
>>> print >> open("somefile", "w+"), "Hello World"
```
#25. 隱藏特性 9, Python3中的元組unpack
```
>>> a, b, *rest = range(10)

>>> a

0

>>> b

1

>>> rest

[2, 3, 4, 5, 6, 7, 8, 9]

>>>

>>> first, second, *rest, last = range(10)

>>> first

0

>>> second

1

>>> last

9

>>> rest

[2, 3, 4, 5, 6, 7, 8]
```
#26. 隱藏特性 10,pow的第三個(gè)參數(shù)
其實(shí)第三個(gè)參數(shù)是來(lái)求模的: pow(x, y, z) == (x ** y) % z,注意,內(nèi)置的 pow 和 math.pow 并不是一個(gè)函數(shù),后者只接受2個(gè)參數(shù)。
```
>>> pow(4, 2, 2)

0

>>> pow(4, 2, 3)

1
```
#27. 隱藏特性 11,enumerate還有第二個(gè)參數(shù)?
enumerate 很贊,可以給我們索引和序列值的對(duì), 但是它還有第二個(gè)參數(shù),這個(gè)參數(shù)用來(lái): 指明索引的起始值。
```
>>> lst = ["a", "b", "c"]

>>> list(enumerate(lst, 1))

[(1, 'a'), (2, 'b'), (3, 'c')]
```
#28. 隱藏特性 12,顯式的聲明一個(gè)集合
```
在Python 2.7 之后可以這么聲明一個(gè)集合。
>>> {1,2,3}

set([1, 2, 3])
```
#29. 隱藏特性 13,用切片來(lái)刪除序列的某一段
```
>>> a = [1, 2, 3, 4, 5, 6, 7]

>>> a[1:4] = []

>>> a

[1, 5, 6, 7] 
```
當(dāng)然用 del a[1:4] 也是可以的,去除偶數(shù)項(xiàng)(偶數(shù)索引的):
```
>>> a = [0, 1, 2, 3, 4, 5, 6, 7]

>>> del a[::2]

>>> a

[1, 3, 5, 7]
```
#30. 隱藏特性 14,isinstance可以接收一個(gè)元組
這個(gè)真的鮮為人知, 我們可以用 isinstance(x, (float, int)) 來(lái)判斷 x 是不是數(shù),也就是那個(gè)元組里面是 或 的關(guān)系,只要是其中一個(gè)的實(shí)例就返回 True。
```
>>> isinstance(1, (float, int))

True

>>> isinstance(1.3, (float, int))

True

>>> isinstance("1.3", (float, int))

False
```
#31. 讓關(guān)鍵代碼依賴于外部包
雖然Python讓許多編程任務(wù)變得容易,但它可能并不總能為緊急的任務(wù)提供最佳性能。你可以為緊急的任務(wù)使用C、C++或機(jī)器語(yǔ)言編寫的外部包,這樣可以提高應(yīng)用程序的性能。這些包都是不能跨平臺(tái)的,這意味著你需要根據(jù)你正在使用的平臺(tái),尋找合適的包。簡(jiǎn)而言之,這個(gè)方案放棄了一些應(yīng)用程序的可移植性,以換取只有在特定主機(jī)上直接編程才能獲得的程序性能。這里有一些你應(yīng)該考慮加入到你的“性能兵工廠”的包:
```
Cython
PyInlne
PyPy
Pyrex
```
這些包以不同的方式提高性能。例如,Pyrex能夠擴(kuò)展Python所能做的事情,例如使用C的數(shù)據(jù)類型來(lái)讓內(nèi)存任務(wù)更加有效或直接。PyInIne讓你在Python應(yīng)用程序中直接使用C代碼。程序中的內(nèi)聯(lián)代碼單獨(dú)編譯,但它在利用C語(yǔ)言所能提供的效率的同時(shí),也讓所有的代碼都在同一個(gè)地方。
#32. 排序時(shí)使用鍵(key)
有很多老的Python排序代碼,它們?cè)谀銊?chuàng)建一個(gè)自定義的排序時(shí)花費(fèi)你的時(shí)間,但在運(yùn)行時(shí)確實(shí)能加速執(zhí)行排序過(guò)程。元素排序的最好方法是盡可能使用鍵(key)和默認(rèn)的sort()排序方法。例如,考慮下面的代碼:
```
import operator

somelist = [(1, 5, 8), (6, 2, 4), (9, 7, 5)]

somelist.sort(key=operator.itemgetter(0))

somelist

#Output = [(1, 5, 8), (6, 2, 4), (9, 7, 5)]

somelist.sort(key=operator.itemgetter(1))

somelist

#Output = [(6, 2, 4), (1, 5, 8), (9, 7, 5)]

somelist.sort(key=operator.itemgetter(2))

somelist
```
每一個(gè)實(shí)例中,根據(jù)你選擇的作為key參數(shù)部分的索引,數(shù)組進(jìn)行了排序。類似于利用數(shù)字進(jìn)行排序,這種方法同樣適用于利用字符串排序。
#33. 優(yōu)化循環(huán)
每種編程語(yǔ)言都會(huì)強(qiáng)調(diào)需要優(yōu)化循環(huán)。當(dāng)使用Python的時(shí)候,你可以依靠大量的技巧使得循環(huán)運(yùn)行得更快。然而,開(kāi)發(fā)者經(jīng)常漏掉的一個(gè)方法是:避免在一個(gè)循環(huán)中使用點(diǎn)操作。例如,考慮下面的代碼:
```
lowerlist = ['this', 'is', 'lowercase']

upper = str.upper

upperlist = []

append = upperlist.append

for word in lowerlist:

 append(upper(word))

 print(upperlist)

 #Output = ['THIS', 'IS', 'LOWERCASE']
```
每一次你調(diào)用方法str.upper,Python都會(huì)求該方法的值。然而,如果你用一個(gè)變量代替求得的值,值就變成了已知的,Python就可以更快地執(zhí)行任務(wù)。優(yōu)化循環(huán)的關(guān)鍵,是要減少Python在循環(huán)內(nèi)部執(zhí)行的工作量,因?yàn)镻ython原生的解釋器在那種情況下,真的會(huì)減緩執(zhí)行的速度。
(注意:優(yōu)化循環(huán)的方法有很多,這只是其中的一個(gè)。例如,許多程序員都會(huì)說(shuō),列表推導(dǎo)是在循環(huán)中提高執(zhí)行速度的最好方式。這里的關(guān)鍵是,優(yōu)化循環(huán)是程序取得更高的執(zhí)行速度的更好方式之一。)
#34. 嘗試多種編碼方法
如果每次你創(chuàng)建一個(gè)應(yīng)用程序都是用相同的編碼方法,幾乎肯定會(huì)導(dǎo)致一些你的應(yīng)用程序比它能夠達(dá)到的運(yùn)行效率慢的情況。作為分析過(guò)程的一部分,你可以嘗試一些實(shí)驗(yàn)。例如,在一個(gè)字典中管理一些元素,你可以采用安全的方法確定元素是否已經(jīng)存在并更新,或者你可以直接添加元素,然后作為異常處理該元素不存在情況。考慮第一個(gè)編碼的例子:
```
n = 16

myDict = {}

for i in range(0, n):

 char = 'abcd'[i%4]

 if char not in myDict:

 myDict[char] = 0

 myDict[char] += 1

 print(myDict)
```
這段代碼通常會(huì)在myDict開(kāi)始為空時(shí)運(yùn)行得更快。然而,當(dāng)mydict通常被數(shù)據(jù)填充(或者至少大部分被充填)時(shí),另一種方法效果更好。
```
n = 16

myDict = {}

for i in range(0, n):

 char = 'abcd'[i%4]

 try:

 myDict[char] += 1

 except KeyError:

 myDict[char] = 1

 print(myDict)
```
兩種情況下具有相同的輸出:{‘d’: 4, ‘c’: 4, ‘b’: 4, ‘a(chǎn)’: 4}。唯一的不同是這個(gè)輸出是如何得到的。跳出固定的思維模式,創(chuàng)造新的編碼技巧,能夠幫助你利用你的應(yīng)用程序獲得更快的結(jié)果。
#35. 使用列表推導(dǎo)式
一個(gè)列表推導(dǎo)式包含以下幾個(gè)部分:
一個(gè)輸入序列
一個(gè)表示輸入序列成員的變量
一個(gè)可選的斷言表達(dá)式
一個(gè)將輸入序列中滿足斷言表達(dá)式的成員變換成輸出列表成員的輸出表達(dá)式
```
num = [1, 4, -5, 10, -7, 2, 3, -1]

filtered_and_squared = []

 

for number in num:

    if number > 0:

        filtered_and_squared.append(number ** 2)

print filtered_and_squared

 

# [1, 16, 100, 4, 9]
```
而如果使用filter、lambda和map函數(shù),則能夠?qū)⒋a大大簡(jiǎn)化:
```
num = [1, 4, -5, 10, -7, 2, 3, -1]

filtered_and_squared = map(lambda x: x ** 2, filter(lambda x: x > 0, num))

print filtered_and_squared

 

# [1, 16, 100, 4, 9]

## 更簡(jiǎn)化的一種寫法 

num = [1, 4, -5, 10, -7, 2, 3, -1]

filtered_and_squared = [ x**2 for x in num if x > 0]

print filtered_and_squared

 

# [1, 16, 100, 4, 9]
```
![comprehension.jpg](http://upload-images.jianshu.io/upload_images/2007846-2e34c1bb83795127.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
列表推導(dǎo)也可能會(huì)有一些負(fù)面效應(yīng),那就是整個(gè)列表必須一次性加載于內(nèi)存之中,這對(duì)上面舉的例子而言不是問(wèn)題,甚至擴(kuò)大若干倍之后也都不是問(wèn)題。但是總會(huì)達(dá)到極限,內(nèi)存總會(huì)被用完。
針對(duì)上面的問(wèn)題,生成器(Generator)能夠很好的解決。生成器表達(dá)式不會(huì)一次將整個(gè)列表加載到內(nèi)存之中,而是生成一個(gè)生成器對(duì)象(Generator objector),所以一次只加載一個(gè)列表元素。
生成器表達(dá)式同列表推導(dǎo)式有著幾乎相同的語(yǔ)法結(jié)構(gòu),區(qū)別在于生成器表達(dá)式是被圓括號(hào)包圍,而不是方括號(hào):
```
num = [1, 4, -5, 10, -7, 2, 3, -1]

filtered_and_squared = ( x**2 for x in num if x > 0 )

print filtered_and_squared

 

# <generator object <genexpr> at 0x00583E18>

 

for item in filtered_and_squared:

    print item

 

# 1, 16, 100 4,9
```
這比列表推導(dǎo)效率稍微提高一些,讓我們?cè)僖淮胃脑煲幌麓a:
```
num = [1, 4, -5, 10, -7, 2, 3, -1]

 

def square_generator(optional_parameter):

    return (x ** 2 for x in num if x > optional_parameter)

 

print square_generator(0)

# <generator object <genexpr> at 0x004E6418>

 

# Option I

for k in square_generator(0):

    print k

# 1, 16, 100, 4, 9

 

# Option II

g = list(square_generator(0))

print g

# [1, 16, 100, 4, 9]
```
除非特殊的原因,應(yīng)該經(jīng)常在代碼中使用生成器表達(dá)式。但除非是面對(duì)非常大的列表,否則是不會(huì)看出明顯區(qū)別的。 再來(lái)看一個(gè)通過(guò)兩階列表推導(dǎo)式遍歷目錄的例子:
```
import os

def tree(top):

    for path, names, fnames in os.walk(top):

        for fname in fnames:

            yield os.path.join(path, fname)

 

for name in tree('C:\Users\XXX\Downloads\Test'):

    print name
```
#36. 裝飾器(Decorators)
裝飾器為我們提供了一個(gè)增加已有函數(shù)或類的功能的有效方法。聽(tīng)起來(lái)是不是很像Java中的面向切面編程(Aspect-Oriented Programming)概念??jī)烧叨己芎?jiǎn)單,并且裝飾器有著更為強(qiáng)大的功能。舉個(gè)例子,假定你希望在一個(gè)函數(shù)的入口和退出點(diǎn)做一些特別的操作(比如一些安全、追蹤以及鎖定等操作)就可以使用裝飾器。
裝飾器是一個(gè)包裝了另一個(gè)函數(shù)的特殊函數(shù):主函數(shù)被調(diào)用,并且其返回值將會(huì)被傳給裝飾器,接下來(lái)裝飾器將返回一個(gè)包裝了主函數(shù)的替代函數(shù),程序的其他部分看到的將是這個(gè)包裝函數(shù)。
```
import time

from functools import wraps

 

def timethis(func):

    '''

    Decorator that reports the execution time.

    '''

    @wraps(func)

    def wrapper(*args, **kwargs):

        start = time.time()

        result = func(*args, **kwargs)

        end = time.time()

        print(func.__name__, end-start)

        return result

    return wrapper

 

@timethis

def countdown(n):

    while n > 0:

        n -= 1

 

countdown(100000)

 

# ('countdown', 0.006999969482421875)
```
#37. 上下文管理庫(kù)(ContextLib)
contextlib模塊包含了與上下文管理器和with聲明相關(guān)的工具。通常如果你想寫一個(gè)上下文管理器,則你需要定義一個(gè)類包含__enter__方法以及__exit__方法,例如:
```
import time

class demo:

    def __init__(self, label):

        self.label = label

 

    def __enter__(self):

        self.start = time.time()

 

    def __exit__(self, exc_ty, exc_val, exc_tb):

        end = time.time()

        print('{}: {}'.format(self.label, end - self.start))
```
完整的例子在此:
```
import time

 

class demo:

    def __init__(self, label):

        self.label = label

 

    def __enter__(self):

        self.start = time.time()

 

    def __exit__(self, exc_ty, exc_val, exc_tb):

        end = time.time()

        print('{}: {}'.format(self.label, end - self.start))

 

with demo('counting'):

    n = 10000000

    while n > 0:

        n -= 1

 

# counting: 1.36000013351
```
上下文管理器被with聲明所激活,這個(gè)API涉及到兩個(gè)方法。
__enter__方法,當(dāng)執(zhí)行流進(jìn)入with代碼塊時(shí),__enter__方法將執(zhí)行。并且它將返回一個(gè)可供上下文使用的對(duì)象。
當(dāng)執(zhí)行流離開(kāi)with代碼塊時(shí),__exit__方法被調(diào)用,它將清理被使用的資源。

利用@contextmanager裝飾器改寫上面那個(gè)例子:
```
from contextlib import contextmanager

import time

 

@contextmanager

def demo(label):

    start = time.time()

    try:

        yield

    finally:

        end = time.time()

        print('{}: {}'.format(label, end - start))

 

with demo('counting'):

    n = 10000000

    while n > 0:

        n -= 1

 

# counting: 1.32399988174
```
看上面這個(gè)例子,函數(shù)中yield之前的所有代碼都類似于上下文管理器中__enter__方法的內(nèi)容。而yield之后的所有代碼都如__exit__方法的內(nèi)容。如果執(zhí)行過(guò)程中發(fā)生了異常,則會(huì)在yield語(yǔ)句觸發(fā)。
#38. 描述器(Descriptors)
描述器決定了對(duì)象屬性是如何被訪問(wèn)的。描述器的作用是定制當(dāng)你想引用一個(gè)屬性時(shí)所發(fā)生的操作。
構(gòu)建描述器的方法是至少定義以下三個(gè)方法中的一個(gè)。需要注意,下文中的instance是包含被訪問(wèn)屬性的對(duì)象實(shí)例,而owner則是被描述器修辭的類。
**get**(self, instance, owner) – 這個(gè)方法是當(dāng)屬性被通過(guò)(value = obj.attr)的方式獲取時(shí)調(diào)用,這個(gè)方法的返回值將被賦給請(qǐng)求此屬性值的代碼部分。 **set**(self, instance, value) – 這個(gè)方法是當(dāng)希望設(shè)置屬性的值(obj.attr = ‘value’)時(shí)被調(diào)用,該方法不會(huì)返回任何值。 **delete**(self, instance) – 當(dāng)從一個(gè)對(duì)象中刪除一個(gè)屬性時(shí)(del obj.attr),調(diào)用此方法。 譯者注:對(duì)于instance和owner的理解,考慮以下代碼:
```
class Celsius(object):

    def __init__(self, value=0.0):

        self.value = float(value)

    def __get__(self, instance, owner):

        return self.value

    def __set__(self, instance, value):

        self.value = float(value)

 

class Temperature(object):

    celsius = Celsius()

 

temp=Temperature()

temp.celsius #calls Celsius.__get__
```
#39. Zipping and unzipping lists and iterables
```
>>> a = [1, 2, 3]

>>> b = ['a', 'b', 'c']

>>> z = zip(a, b)

>>> z

[(1, 'a'), (2, 'b'), (3, 'c')]

>>> zip(*z)

[(1, 2, 3), ('a', 'b', 'c')]
```
#40. Grouping adjacent list items using zip
```
>>> a = [1, 2, 3, 4, 5, 6]

>>> # Using iterators

>>> group_adjacent = lambda a, k: zip(*([iter(a)] * k))

>>> group_adjacent(a, 3)

[(1, 2, 3), (4, 5, 6)]

>>> group_adjacent(a, 2)

[(1, 2), (3, 4), (5, 6)]

>>> group_adjacent(a, 1)

[(1,), (2,), (3,), (4,), (5,), (6,)]

>>> # Using slices

>>> from itertools import islice

>>> group_adjacent = lambda a, k: zip(*(islice(a, i, None, k) for i in range(k)))

>>> group_adjacent(a, 3)

[(1, 2, 3), (4, 5, 6)]

>>> group_adjacent(a, 2)

[(1, 2), (3, 4), (5, 6)]

>>> group_adjacent(a, 1)

[(1,), (2,), (3,), (4,), (5,), (6,)]
```
#41. Sliding windows (n-grams) using zip and iterators
```
>>> from itertools import islice

>>> def n_grams(a, n):

... z = (islice(a, i, None) for i in range(n))

... return zip(*z)

...

>>> a = [1, 2, 3, 4, 5, 6]

>>> n_grams(a, 3)

[(1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6)]

>>> n_grams(a, 2)

[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

>>> n_grams(a, 4)

[(1, 2, 3, 4), (2, 3, 4, 5), (3, 4, 5, 6)]
```
#42. Inverting a dictionary using zip
```
>>> m = {'a': 1, 'b': 2, 'c': 3, 'd': 4}

>>> m.items()

[('a', 1), ('c', 3), ('b', 2), ('d', 4)]

>>> zip(m.values(), m.keys())

[(1, 'a'), (3, 'c'), (2, 'b'), (4, 'd')]

>>> mi = dict(zip(m.values(), m.keys()))

>>> mi

{1: 'a', 2: 'b', 3: 'c', 4: 'd'}
```
#43. Flattening lists
```
>>> a = [[1, 2], [3, 4], [5, 6]]

>>> list(itertools.chain.from_iterable(a))

[1, 2, 3, 4, 5, 6]

>>> sum(a, [])

[1, 2, 3, 4, 5, 6]

>>> [x for l in a for x in l]

[1, 2, 3, 4, 5, 6]

>>> a = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]

>>> [x for l1 in a for l2 in l1 for x in l2]

[1, 2, 3, 4, 5, 6, 7, 8]

>>> a = [1, 2, [3, 4], [[5, 6], [7, 8]]]

>>> flatten = lambda x: [y for l in x for y in flatten(l)] if type(x) is list else [x]

>>> flatten(a)

[1, 2, 3, 4, 5, 6, 7, 8]
```
#44. Dictionary comprehensions
```
>>> m = {x: x ** 2 for x in range(5)}

>>> m

{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

>>> m = {x: 'A' + str(x) for x in range(10)}

>>> m

{0: 'A0', 1: 'A1', 2: 'A2', 3: 'A3', 4: 'A4', 5: 'A5', 6: 'A6', 7: 'A7', 8: 'A8', 9: 'A9'}
```
#45. 常犯錯(cuò)誤,濫用表達(dá)式作為函數(shù)參數(shù)默認(rèn)值
Python允許開(kāi)發(fā)者指定一個(gè)默認(rèn)值給函數(shù)參數(shù),雖然這是該語(yǔ)言的一個(gè)特征,但當(dāng)參數(shù)可變時(shí),很容易導(dǎo)致混亂,例如,下面這段函數(shù)定義:
```
>>> def foo(bar=[]): # bar is optional and defaults to [] if not specified

... bar.append("baz") # but this line could be problematic, as we'll see...

... return bar
```
在上面這段代碼里,一旦重復(fù)調(diào)用foo()函數(shù)(沒(méi)有指定一個(gè)bar參數(shù)),那么將一直返回’bar’,因?yàn)闆](méi)有指定參數(shù),那么foo()每次被調(diào)用的時(shí)候,都會(huì)賦予[]。下面來(lái)看看,這樣做的結(jié)果:
```
>>> foo()

["baz"]

>>> foo()

["baz", "baz"]

>>> foo()

["baz", "baz", "baz"]
```
解決方案:
```
>>> def foo(bar=None):

... if bar is None: # or if not bar:

... bar = []

... bar.append("baz")

... return bar

...

>>> foo()

["baz"]

>>> foo()

["baz"]

>>> foo()

["baz"]
```
#46. 誤解Python規(guī)則范圍
Python的作用域解析是基于LEGB規(guī)則,分別是Local、Enclosing、Global、Built-in。實(shí)際上,這種解析方法也有一些玄機(jī),看下面這個(gè)例子:
```
>>> x = 10

>>> def foo():

... x += 1

... print x

...

>>> foo()

Traceback (most recent call last):

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

 File "<stdin>", line 2, in foo

UnboundLocalError: local variable 'x' referenced before assignment
```
許多人會(huì)感動(dòng)驚訝,當(dāng)他們?cè)诠ぷ鞯暮瘮?shù)體里添加一個(gè)參數(shù)語(yǔ)句,會(huì)在先前工作的代碼里報(bào)UnboundLocalError錯(cuò)誤( 點(diǎn)擊這里查看更詳細(xì)描述)。 在使用列表時(shí),開(kāi)發(fā)者是很容易犯這種錯(cuò)誤的,看看下面這個(gè)例子:
```
>>> lst = [1, 2, 3]

>>> def foo1():

... lst.append(5) # This works ok...

...

>>> foo1()

>>> lst

[1, 2, 3, 5]

>>> lst = [1, 2, 3]

>>> def foo2():

... lst += [5] # ... but this bombs!

...

>>> foo2()

Traceback (most recent call last):

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

 File "<stdin>", line 2, in foo

UnboundLocalError: local variable 'lst' referenced before assignment
```
為什么foo2失敗而foo1運(yùn)行正常? 答案與前面那個(gè)例子是一樣的,但又有一些微妙之處。foo1沒(méi)有賦值給lst,而foo2賦值了。lst += [5]實(shí)際上就是lst = lst + [5],試圖給lst賦值(因此,假設(shè)Python是在局部作用域里)。然而,我們正在尋找指定給lst的值是基于lst本身,其實(shí)尚未確定。
#47. 修改遍歷列表
```
>>> odd = lambda x : bool(x % 2)

>>> numbers = [n for n in range(10)]

>>> for i in range(len(numbers)):

... if odd(numbers[i]):

... del numbers[i] # BAD: Deleting item from a list while iterating over it

...

Traceback (most recent call last):

 File "<stdin>", line 2, in <module>

IndexError: list index out of range
```
在遍歷的時(shí)候,對(duì)列表進(jìn)行刪除操作,這是很低級(jí)的錯(cuò)誤。稍微有點(diǎn)經(jīng)驗(yàn)的人都不會(huì)犯。 對(duì)上面的代碼進(jìn)行修改,正確地執(zhí)行:
```
>>> odd = lambda x : bool(x % 2)

>>> numbers = [n for n in range(10)]

>>> numbers[:] = [n for n in numbers if not odd(n)] # ahh, the beauty of it all

>>> numbers

[0, 2, 4, 6, 8]
```
#48. 合理使用copy與deepcopy
對(duì)于dict和list等數(shù)據(jù)結(jié)構(gòu)的對(duì)象,直接賦值使用的是引用的方式。而有些情況下需要復(fù)制整個(gè)對(duì)象,這時(shí)可以使用copy包里的copy和deepcopy,這兩個(gè)函數(shù)的不同之處在于后者是遞歸復(fù)制的。效率也不一樣:(以下程序在ipython中運(yùn)行)
timeit后面的-n表示運(yùn)行的次數(shù),后兩行對(duì)應(yīng)的是兩個(gè)timeit的輸出,下同。由此可見(jiàn)后者慢一個(gè)數(shù)量級(jí)。
```
import copy

a = range(100000)

%timeit -n 10 copy.copy(a) # 運(yùn)行10次 copy.copy(a)

%timeit -n 10 copy.deepcopy(a)

10 loops, best of 3: 1.55 ms per loop

10 loops, best of 3: 151 ms per loop
```
#49. 合理使用生成器(generator)和yield
```
%timeit -n 100 a = (i for i in range(100000))

%timeit -n 100 b = [i for i in range(100000)]

100 loops, best of 3: 1.54 ms per loop

100 loops, best of 3: 4.56 ms per loop
```
使用()得到的是一個(gè)generator對(duì)象,所需要的內(nèi)存空間與列表的大小無(wú)關(guān),所以效率會(huì)高一些。在具體應(yīng)用上,比如set(i for i in range(100000))會(huì)比set([i for i in range(100000)])快。
但是對(duì)于需要循環(huán)遍歷的情況:
```
%timeit -n 10 for x in (i for i in range(100000)): pass

%timeit -n 10 for x in [i for i in range(100000)]: pass

10 loops, best of 3: 6.51 ms per loop

10 loops, best of 3: 5.54 ms per loop
```
后者的效率反而更高,但是如果循環(huán)里有break,用generator的好處是顯而易見(jiàn)的。yield也是用于創(chuàng)建generator:
#50. 使用級(jí)聯(lián)比較x < y < z
```
x, y, z = 1,2,3

%timeit -n 1000000 if x < y < z:pass

%timeit -n 1000000 if x < y and y < z:pass

1000000 loops, best of 3: 101 ns per loop

1000000 loops, best of 3: 121 ns per loop
```
x < y < z效率略高,而且可讀性更好。
#51. while 1 比 while True 更快
```
def while_1():

 n = 100000

 while 1:

 n -= 1

 if n <= 0: break

def while_true():

 n = 100000

 while True:

 n -= 1

 if n <= 0: break 

m, n = 1000000, 1000000 

%timeit -n 100 while_1()

%timeit -n 100 while_true()

100 loops, best of 3: 3.69 ms per loop

100 loops, best of 3: 5.61 ms per loop
```
while 1 比 while true快很多,原因是在python2.x中,True是一個(gè)全局變量,而非關(guān)鍵字。
#52. 使用**而不是pow
```
%timeit -n 10000 c = pow(2,20)

%timeit -n 10000 c = 2**20

10000 loops, best of 3: 284 ns per loop

10000 loops, best of 3: 16.9 ns per loop
```
#53. 使用 cProfile, cStringIO 和 cPickle等用c實(shí)現(xiàn)相同功能(分別對(duì)應(yīng)profile, StringIO, pickle)的包
```
import cPickle

import pickle

a = range(10000)

%timeit -n 100 x = cPickle.dumps(a)

%timeit -n 100 x = pickle.dumps(a)

100 loops, best of 3: 1.58 ms per loop

100 loops, best of 3: 17 ms per loop
```
由c實(shí)現(xiàn)的包,速度快10倍以上!
#54. 使用最佳的反序列化方式
下面比較了eval, cPickle, json方式三種對(duì)相應(yīng)字符串反序列化的效率,可見(jiàn)json比cPickle快近3倍,比eval快20多倍。
```
import json

import cPickle

a = range(10000)

s1 = str(a)

s2 = cPickle.dumps(a)

s3 = json.dumps(a)

%timeit -n 100 x = eval(s1)

%timeit -n 100 x = cPickle.loads(s2)

%timeit -n 100 x = json.loads(s3)

100 loops, best of 3: 16.8 ms per loop

100 loops, best of 3: 2.02 ms per loop

100 loops, best of 3: 798 μs per loop
```
#55. 怎么才算精通python
這個(gè)問(wèn)題比較難回答,我是看 [怎么樣才算是精通 Python](http://www.zhihu.com/question/19794855) 這個(gè)知乎問(wèn)答,按照自己的看法整理了一些觀點(diǎn)。不要問(wèn)我是按什么標(biāo)準(zhǔn)整理的,我只能說(shuō),整理的這些點(diǎn),第一,在我看來(lái)都說(shuō)得不錯(cuò);第二,我自己都會(huì)去按照這些點(diǎn)來(lái)看看自己離 “精通” python還有多遠(yuǎn)。
1. 熟悉語(yǔ)法以及原聲數(shù)據(jù)結(jié)構(gòu)
1. 熟悉基本實(shí)現(xiàn)中的性能特點(diǎn),就是知道什么操作會(huì)慢
1. 會(huì)使用profile以及基于profile的性能分析工具
1. 會(huì)使用運(yùn)行時(shí)編譯和靜態(tài)編譯的工具。pypy,numba,cython,ctypes,original C/C++ extension
1. 熟悉你所在領(lǐng)域的拓展庫(kù),比如我,科學(xué)計(jì)算方面的庫(kù)不要太多,numpy衍生出來(lái)的一大堆大堆
1. 了解基本的編譯過(guò)程,基本的操作系統(tǒng)知識(shí)(只要你C、C++學(xué)的還行就可以了)
1. 要想精通python,寫的代碼首先得pythonic
1. 研讀牛B的開(kāi)源代碼,在這過(guò)程中會(huì)遇到python的許多高階用法
1. 理解裝飾器,生成器,描述符,元類
1. 掌握l(shuí)ist comprehension,
1. 多用內(nèi)置函數(shù):map,reduce,filter,iter,range,divmod,round,chr,enumerate,all,any,slice,zip+

#56. python 猴子補(bǔ)丁相關(guān)
python里有一個(gè)很奇妙的monkey patch,中文叫做猴子補(bǔ)丁,是指的是在運(yùn)行時(shí)動(dòng)態(tài)替換某些已加載的模塊的實(shí)現(xiàn)。第一次了解這個(gè)概念是在使用gevent的時(shí)候,需要把python自帶的socket,os等相關(guān)模塊的實(shí)現(xiàn)改變成異步形式,但同時(shí)不改動(dòng)python的源代碼。
#57. 了解 functools.partial
先參考 [飄逸的python - 偏函數(shù)functools.partial](http://blog.csdn.net/handsomekang/article/details/9712125)
#58. python 中時(shí)間格式轉(zhuǎn)換
![python-time.jpg](http://upload-images.jianshu.io/upload_images/2007846-8cff37eb529a22a5.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
#59. python 一行代碼啟動(dòng) http 服務(wù)器
```
python -mSimpleHTTPServer
```
______________________________________________________________
為了方便大家學(xué)習(xí),我建立了一個(gè)Python交流群,目前群內(nèi)已經(jīng)有157個(gè)小伙伴,學(xué)習(xí)寂寞的小伙伴不妨一起來(lái)玩~群號(hào): 475035830
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,715評(píng)論 19 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,365評(píng)論 25 708
  • http://python.jobbole.com/85231/ 關(guān)于專業(yè)技能寫完項(xiàng)目接著寫寫一名3年工作經(jīng)驗(yàn)的J...
    燕京博士閱讀 7,829評(píng)論 1 118
  • # Python 資源大全中文版 我想很多程序員應(yīng)該記得 GitHub 上有一個(gè) Awesome - XXX 系列...
    aimaile閱讀 26,846評(píng)論 6 427
  • 人真得是一個(gè)復(fù)雜的東西,你要?jiǎng)e人對(duì)你好,自己卻不對(duì)他撐開(kāi)心扉,你對(duì)他敞開(kāi)心扉,卻會(huì)受到無(wú)盡的算計(jì)。 考研失敗之后,...
    良然成閱讀 855評(píng)論 0 0

友情鏈接更多精彩內(nèi)容