Python 技巧總結
1. 枚舉 - enumerate 可以有參數哦
之前我們這樣操作:
i = 0
for item in iterable:
print i, item
i += 1
現在我們這樣操作:
for i, item in enumerate(iterable):
print i, item
enumerate函數還可以接收第二個參數。就像下面這樣:
>>> list(enumerate("abc"))
[(0, "a"), (1, "b"), (2, "c")]
>>> list(enumerate("abc", 1))
[(1, "a"), (2, "b"), (3, "c")]
2. 字典/集合 解析
你也許知道如何進行列表解析,但是可能不知道字典/集合解析。它們簡單易用且高效。就像下面這個例子:
my_dict =
my_set =
# There is only a difference of ":" in both
# 兩者的區別在於字典推導中有冒號
3. 強制浮點除法
from __future__ import division
result = 1/2
# print(result)
# 0.5
4. 對Python表達式求值
我們都知道eval函數,但是我們知道literal_eval函數么?也許很多人都不知道吧。可以用這種操作:
import ast
my_list = ast.literal_eval(expr)
來代替以下這種操作:
expr = "[1, 2, 3]
my_list = eval(expr)
我相信對於大多數人來說這種形式是第一次看見,但是實際上這個在Python中已經存在很長時間了。
5. 字元串/數列 逆序
你可以用以下方法快速逆序排列數列:
>>> 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. 三元運算
三元運算是if-else 語句的快捷操作,也被稱為條件運算。這裡有幾個例子可以供你參考,它們可以讓你的代碼更加緊湊,更加美觀。
[on_true] if [expression] else [on_false]
x, y = 50, 25
small = x if x
7. Python裡面如何拷貝一個對象
標準庫中的copy模塊提供了兩個方法來實現拷貝.一個方法是copy,它返回和參數包含內容一樣的對象.
import copy
new_list = copy.copy(existing_list)
有些時候,你希望對象中的屬性也被複制,可以使用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中如何判斷對象相等
首先是C#中字元串的==和equal方法。
「==」 :
對於內置值類型而言, == 判斷兩個內存值是否相等
對於用戶自定義的值類型而言(Struct), == 需要重載,否則不能使用。
對於引用類型而言,默認是同一引用才返回true,但是系統重載了很多引用類型的 == (比如下文提到的string),所以c#中引用類型的比較並不建議使用 ==。
「equals」 :
對於值類型而言, 內存相等才返回true。
對於引用類型而言,指向同一個引用才算相等。
但是比較特殊的是字元串String,是一個特殊的引用型類型,在C#語言中,重載了string的equals()方法,使string對象用起來就像是值類型一樣。
python中的 ==
python中的對象包含三要素:id, type, value
id 用來標識唯一一個對象,type標識對象的類型,value用來設置對象的值
is 判斷是否是一個對象,使用id來判斷的。
== 是判斷a對象的值是否是b對象的值,默認調用它的__eq__方法。
9. 命名技巧
今天閱讀代碼,發現一個不錯的函數命名方式:
def request(_argv):
就是把所有的參數前面都加上_下劃線,這樣你在函數體中,一眼就可以看出那些是局部變數,那些是作為參數傳入的,類似把全局變數前面加上g。
10. 開發者工具集錦
pydoc: 模塊可以根據源代碼中的docstrings為任何可導入模塊生成格式良好的文檔。
doctest模塊:該模塊可以從源代碼或獨立文件的例子中抽取出測試用例。
unittest模塊:該模塊是一個全功能的自動化測試框架,該框架提供了對測試準備(test fixtures), 預定義測試集(predefined test suite)以及測試發現(test discovery)的支持。
trace:模塊可以監控Python執行程序的方式,同時生成一個報表來顯示程序的每一行執行的次數。這些信息可以用來發現未被自動化測試集所覆蓋的程序執行路徑,也可以用來研究程序調用圖,進而發現模塊之間的依賴關係。編寫並執行測試可以發現絕大多數程序中的問題,Python使得debug工作變得更加簡單,這是因為在大部分情況下,Python都能夠將未被處理的錯誤列印到控制台中,我們稱這些錯誤信息為traceback。如果程序不是在文本控制台中運行的,traceback也能夠將錯誤信息輸出到日誌文件或是消息對話框中。當標準的traceback無法提供足夠的信息時,可以使用cgitb 模塊來查看各級棧和源代碼上下文中的詳細信息,比如局部變數。cgitb模塊還能夠將這些跟蹤信息以HTML的形式輸出,用來報告web應用中的錯誤。
pdb:該模塊可以顯示出程序在錯誤產生時的執行路徑,同時可以動態地調整對象和代碼進行調試。
profile, timeit: 開發者可以使用profile以及timit模塊來測試程序的速度,找出程序中到底是哪裡很慢,進而對這部分代碼獨立出來進行調優的工作。
compileall: Python程序是通過解釋器執行的,解釋器的輸入是原有程序的位元組碼編譯版本。這個位元組碼編譯版本可以在程序執行時動態地生成,也可以在程序打包的時候就生成。compileall模塊可以處理程序打包的事宜,它暴露出了打包相關的介面,該介面能夠被安裝程序和打包工具用來生成包含模塊位元組碼的文件。同時,在開發環境中,compileall模塊也可以用來驗證源文件是否包含了語法錯誤。
YAPF:Google開源的Python代碼格式化工具。
iPDB: iPDB是一個極好的工具,我已經用它查出了很多匪夷所思的bug。pip install ipdb 安裝該工具,然後在你的代碼中import ipdb; ipdb.set_trace(),然後你會在你的程序運行時,獲得一個很好的互動式提示。它每次執行程序的一行並且檢查變數。
pycallgraph: 在一些場合,我使用pycallgraph來追蹤性能問題。它可以創建函數調用時間和次數的圖表。
objgraph: objgraph對於查找內存泄露非常有用。
11. Python代碼微優化之加快查找
collections.OrderedDict類:
def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
if key not in self:
Coot = self.__root
last = root[0]
last[1] = root[0] = self.__map[key] = [last, root, key]
return dict_setitem(self, key, value)
注意最後一個參數:dict_setitem=dict.setitem。如果你仔細想就會感覺有道理。將值關聯到鍵上,你只需要給__setitem__傳遞三個參數:要設置的鍵,與鍵關聯的值,傳遞給內建dict類的__setitem__類方法。等會,好吧,也許最後一個參數沒什麼意義。 最後一個參數其實是將一個函數綁定到局部作用域中的一個函數上。具體是通過將dict.__setitem__賦值為參數的默認值。這裡還有另一個例子:
這裡我們做同樣的事情,把本來將會在內建命名空間中的對象綁定到局部作用域中去。因此,python將會使用LOCAL_FAST而不是LOAD_GLOBAL(全局查找)。那麼這到底有多快呢?我們做個簡單的測試:
$ 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
換句話說,大概有11.9%的提升 [2]。比我在文章開始處承諾的5%還多!
12. 包管理
Python世界最棒的地方之一,就是大量的第三方程序包。同樣,管理這些包也非常容易。按照慣例,會在 requirements.txt 文件中列出項目所需要的包。每個包佔一行,通常還包含版本號
pelican==3.3
Markdown
pelican-extended-sitemap==1.0.0
13. Python函數參數默認值的陷阱和原理深究
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]
可見代碼運行結果並不和我們預期的一樣。list_2在函數的第二次調用時並沒有得到一個新的list並填入2,而是在第一次調用結果的基礎上append了一個2。為什麼會發生這樣在其他編程語言中簡直就是設計bug一樣的問題呢?
可見如果參數默認值是在函數編譯compile階段就已經被確定。之後所有的函數調用時,如果參數不顯示的給予賦值,那麼所謂的參數默認值不過是一個指向那個在compile階段就已經存在的對象的指針。如果調用函數時,沒有顯示指定傳入參數值得話。那麼所有這種情況下的該參數都會作為編譯時創建的那個對象的一種別名存在。如果參數的默認值是一個不可變(Imuttable)數值,那麼在函數體內如果修改了該參數,那麼參數就會重新指向另一個新的不可變值。而如果參數默認值是和本文最開始的舉例一樣,是一個可變對象(Muttable),那麼情況就比較糟糕了。所有函數體內對於該參數的修改,實際上都是對compile階段就已經確定的那個對象的修改。
14. 單下劃線(_)
1、在解釋器中:在這種情況下,「_」代表互動式解釋器會話中上一條執行的語句的結果。這種用法首先被標準CPython解釋器採用,然後其他類型的解釋器也先後採用。
>>> _ Traceback (most recent call last):
File "", line 1, in
NameError: name "_" is not defined
42
>>> "alright!" if _ else ":("
"alright!">>>
"alright!"
2、作為一個名稱:這與上面一點稍微有些聯繫,此時「」作為臨時性的名稱使用。這樣,當其他人閱讀你的代碼時將會知道,你分配了一個特定的名稱,但是並不會在後面再次用到該名稱。例如,下面的例子中,你可能對循環計數中的實際值並不感興趣,此時就可以使用「」。
n = 42
for _ in range(n):
do_something()
3、國際化:也許你也曾看到」_「會被作為一個函數來使用。這種情況下,它通常用於實現國際化和本地化字元串之間翻譯查找的函數名稱,這似乎源自並遵循相應的C約定。例如,在Django文檔「轉換」章節中,你將能看到如下代碼:
from django.http import HttpResponse
def my_view(request):
output = _("Welcome to my site.").
return HttpResponse(output)
可以發現,場景二和場景三中的使用方法可能會相互衝突,所以我們需要避免在使用「」作為國際化查找轉換功能的代碼塊中同時使用「」作為臨時名稱。
15. 名稱前的單下劃線(如:_shahriar)
程序員使用名稱前的單下劃線,用於指定該名稱屬性為「私有」。這有點類似於慣例,為了使其他人(或你自己)使用這些代碼時將會知道以「_」開頭的名稱只供內部使用。正如Python文檔中所述:
以下劃線 __ 為前綴的名稱(如_pam)應該被視為API中非公開的部分(不管是函數、方法還是數據成員)。此時,應該將它們看作是一種實現細節,在修改它們時無需對外部通知。
正如上面所說,這確實類似一種慣例,因為它對解釋器來說確實有一定的意義,如果你寫了代碼 :
from import *
,那麼以 _ 開頭的名稱都不會被導入,除非模塊或包中的
__all__
列表顯式地包含了它們。了解更多請查看
Importing * in Python
16. 名稱前的雙下劃線(如:__shahriar)
名稱(具體為一個方法名)前雙下劃線 _ 的用法並不是一種慣例,對解釋器來說它有特定的意義。Python中的這種用法是為了避免與子類定義的名稱衝突。Python文檔指出,__spam 這種形式(至少兩個前導下劃線,最多一個後續下劃線)的任何標識符將會被 正如所預料的,「_internal_use」並未改變,而「__method_name」卻被變成了「_ClassName__method_name」。此時,如果你創建A的一個子類B,那麼你將不能輕易地覆寫A中的方法「__method_name」。spam 這種形式原文取代,在這裡 classname 是去掉前導下劃線的當前類名。例如下面的例子:
>>> class A(object):
.. def _internal_use(self):
... pass
... def __method_name(self):
.. pass
...
>>> dir(A())
["_A__method_name", ..., "_internal_use"]
正如所預料的,「_internal_use」並未改變,而「__method_name」卻被變成了「_ClassName__method_name」。此時,如果你創建A的一個子類B,那麼你將不能輕易地覆寫A中的方法「__method_name」。
17. 名稱前後的雙下劃線(如:init)
這種用法表示Python中特殊的方法名。其實,這只是一種慣例,對Python系統來說,這將確保不會與用戶自定義的名稱衝突。通常,你將會覆寫這些方法,並在裡面實現你所需要的功能,以便Python調用它們。例如,當定義一個類時,你經常會覆寫「init」方法。
雖然你也可以編寫自己的特殊方法名,但不要這樣做。
17. 隱藏特性 1,函數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, 鏈式比較操作符
>>> 1
True
>>> 4 > x >=3
True
19. 隱藏特性 3,函數的默認參數
>>> def foo(x=[]):
... x.append(1)
... print x
[1]
[1, 1]
更安全的做法是:
>>> def foo(x=None):
... if x is None:
... x = []
... x.append(1)
... print x
[1]
[1]
20. 隱藏特性 4,字典的get方法
21. 隱藏特性 5,帶關鍵字的格式化
>>> 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 !".format(name="James")
Hello James !
22. 隱藏特性 6,切片操作的步長參數
可以用步長 -1 來反轉鏈表:
>>> a = [1, 2, 3, 4, 5]
>>> a[::2]
[1, 3, 5]
>>> a[::-1]
[5, 4, 3, 2, 1]
23. 隱藏特性 7,嵌套列表推導式
[(i, j) for i in range(3) for j in range(i)]
[(1, 0), (2, 0), (2, 1)]
列表推導構造permutation:
可以用 itertools.permutations 來實現。
24. 隱藏特性 8,print 重定向輸出到文件
注意打開的模式: 「w+」 而不能 「w」 , 當然 「a」 是可以的
>>> print >> open("somefile", "w+"), "Hello World"
25. 隱藏特性 9, Python3中的元組unpack
>>> a, b, *rest = range(10)
[2, 3, 4, 5, 6, 7, 8, 9]
>>> first, second, *rest, last = range(10)
[2, 3, 4, 5, 6, 7, 8]
26. 隱藏特性 10,pow的第三個參數
>>> pow(4, 2, 2)
>>> pow(4, 2, 3)
27. 隱藏特性 11,enumerate還有第二個參數?
enumerate 很贊,可以給我們索引和序列值的對, 但是它還有第二個參數,這個參數用來: 指明索引的起始值。
>>> lst = ["a", "b", "c"]
>>> list(enumerate(lst, 1))
[(1, "a"), (2, "b"), (3, "c")]
28. 隱藏特性 12,顯式的聲明一個集合
在Python 2.7 之後可以這麼聲明一個集合。
set([1, 2, 3])
29. 隱藏特性 13,用切片來刪除序列的某一段
>>> a = [1, 2, 3, 4, 5, 6, 7]
>>> a[1:4] = []
[1, 5, 6, 7]
當然用 del a[1:4] 也是可以的,去除偶數項(偶數索引的)
>>> a = [0, 1, 2, 3, 4, 5, 6, 7]
>>> del a[::2]
[1, 3, 5, 7]
30. 隱藏特性 14,isinstance可以接收一個元組
這個真的鮮為人知, 我們可以用 isinstance(x, (float, int)) 來判斷 x 是不是數,也就是那個元組裡面是 或 的關係,只要是其中一個的實例就返回 True。
>>> isinstance(1, (float, int))
True
>>> isinstance(1.3, (float, int))
True
>>> isinstance("1.3", (float, int))
False
31. 讓關鍵代碼依賴於外部包
雖然Python讓許多編程任務變得容易,但它可能並不總能為緊急的任務提供最佳性能。你可以為緊急的任務使用C、C++或機器語言編寫的外部包,這樣可以提高應用程序的性能。這些包都是不能跨平台的,這意味著你需要根據你正在使用的平台,尋找合適的包。簡而言之,這個方案放棄了一些應用程序的可移植性,以換取只有在特定主機上直接編程才能獲得的程序性能。這裡有一些你應該考慮加入到你的「性能兵工廠」的包:
Cython
PyInlne
PyPy
Pyrex
這些包以不同的方式提高性能。例如,Pyrex能夠擴展Python所能做的事情,例如使用C的數據類型來讓內存任務更加有效或直接。PyInIne讓你在Python應用程序中直接使用C代碼。程序中的內聯代碼單獨編譯,但它在利用C語言所能提供的效率的同時,也讓所有的代碼都在同一個地方。
32. 排序時使用鍵(key)
有很多老的Python排序代碼,它們在你創建一個自定義的排序時花費你的時間,但在運行時確實能加速執行排序過程。元素排序的最好方法是儘可能使用鍵(key)和默認的sort()排序方法。例如,考慮下面的代碼:ghu
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
每一個實例中,根據你選擇的作為key參數部分的索引,數組進行了排序。類似於利用數字進行排序,這種方法同樣適用於利用字元串排序。
33. 優化循環
每種編程語言都會強調需要優化循環。當使用Python的時候,你可以依靠大量的技巧使得循環運行得更快。然而,開發者經常漏掉的一個方法是:避免在一個循環中使用點操作。例如,考慮下面的代碼:
lowerlist = ["this", "is", "lowercase"]
upper = str.upper
upperlist = []
append = upperlist.append
for word in lowerlist:
append(upper(word))
print(upperlist)
#Output = ["THIS", "IS", "LOWERCASE"]
每一次你調用方法str.upper,Python都會求該方法的值。然而,如果你用一個變數代替求得的值,值就變成了已知的,Python就可以更快地執行任務。優化循環的關鍵,是要減少Python在循環內部執行的工作量,因為Python原生的解釋器在那種情況下,真的會減緩執行的速度。
(注意:優化循環的方法有很多,這只是其中的一個。例如,許多程序員都會說,列表推導是在循環中提高執行速度的最好方式。這裡的關鍵是,優化循環是程序取得更高的執行速度的更好方式之一。)
34. 嘗試多種編碼方法
如果每次你創建一個應用程序都是用相同的編碼方法,幾乎肯定會導致一些你的應用程序比它能夠達到的運行效率慢的情況。作為分析過程的一部分,你可以嘗試一些實驗。例如,在一個字典中管理一些元素,你可以採用安全的方法確定元素是否已經存在並更新,或者你可以直接添加元素,然後作為異常處理該元素不存在情況。考慮第一個編碼的例子:
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)
這段代碼通常會在myDict開始為空時運行得更快。然而,當mydict通常被數據填充(或者至少大部分被充填)時,另一種方法效果更好。
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』: 4}。唯一的不同是這個輸出是如何得到的。跳出固定的思維模式,創造新的編碼技巧,能夠幫助你利用你的應用程序獲得更快的結果。
35. 使用列表推導式
一個列表推導式包含以下幾個部分:
一個輸入序列
一個表示輸入序列成員的變數
一個可選的斷言表達式
一個將輸入序列中滿足斷言表達式的成員變換成輸出列表成員的輸出表達式
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函數,則能夠將代碼大大簡化
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]
## 更簡化的一種寫法
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]
列表推導也可能會有一些負面效應,那就是整個列表必須一次性載入於內存之中,這對上面舉的例子而言不是問題,甚至擴大若干倍之後也都不是問題。但是總會達到極限,內存總會被用完。
針對上面的問題,生成器(Generator)能夠很好的解決。生成器表達式不會一次將整個列表載入到內存之中,而是生成一個生成器對象(Generator objector),所以一次只載入一個列表元素。
生成器表達式同列表推導式有著幾乎相同的語法結構,區別在於生成器表達式是被圓括弧包圍,而不是方括弧:
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
# at 0x00583E18>
for item in filtered_and_square:
print item
# 1, 16, 100 4,9
這比列表推導效率稍微提高一些,讓我們再一次改造一下代碼:
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)
# 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]
除非特殊的原因,應該經常在代碼中使用生成器表達式。但除非是面對非常大的列表,否則是不會看出明顯區別的。 再來看一個通過兩階列表推導式遍歷目錄的例子:
import os
def tree(top):
for path, names, fnames in os.walk(top):
for fname in fnames:
for name in tree("C:UsersXXXDownloadsTest"):
print name
36. 裝飾器(Decorators)
裝飾器為我們提供了一個增加已有函數或類的功能的有效方法。聽起來是不是很像Java中的面向切面編程(Aspect-Oriented Programming)概念?兩者都很簡單,並且裝飾器有著更為強大的功能。舉個例子,假定你希望在一個函數的入口和退出點做一些特別的操作(比如一些安全、追蹤以及鎖定等操作)就可以使用裝飾器。
裝飾器是一個包裝了另一個函數的特殊函數:主函數被調用,並且其返回值將會被傳給裝飾器,接下來裝飾器將返回一個包裝了主函數的替代函數,程序的其他部分看到的將是這個包裝函數。
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)
37. 上下文管理庫(ContextLib)
contextlib模塊包含了與上下文管理器和with聲明相關的工具。通常如果你想寫一個上下文管理器,則你需要定義一個類包含__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
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"):
while n > 0:
n -= 1
上下文管理器被with聲明所激活,這個API涉及到兩個方法。 1. __enter__方法,當執行流進入with代碼塊時,__enter__方法將執行。並且它將返回一個可供上下文使用的對象。 2. 當執行流離開with代碼塊時,__exit__方法被調用,它將清理被使用的資源。
利用@contextmanager裝飾器改寫上面那個例子
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"):
while n > 0:
n -= 1
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"):
while n > 0
n -= 1
看上面這個例子,函數中yield之前的所有代碼都類似於上下文管理器中__enter__方法的內容。而yield之後的所有代碼都如__exit__方法的內容。如果執行過程中發生了異常,則會在yield語句觸發。
38. 描述器(Descriptors)
描述器決定了對象屬性是如何被訪問的。描述器的作用是定製當你想引用一個屬性時所發生的操作。
構建描述器的方法是至少定義以下三個方法中的一個。需要注意,下文中的instance是包含被訪問屬性的對象實例,而owner則是被描述器修辭的類。
get(self, instance, owner) – 這個方法是當屬性被通過(value = obj.attr)的方式獲取時調用,這個方法的返回值將被賦給請求此屬性值的代碼部分。set(self, instance, value) – 這個方法是當希望設置屬性的值(obj.attr = 『value』)時被調用,該方法不會返回任何值。delete(self, instance) – 當從一個對象中刪除一個屬性時(del obj.attr),調用此方法。 譯者註:對於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)
[(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,)]
>>> 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 slice
>>> 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,)]
以上是本人對於所學做的總結 希望對小夥伴們有所幫助
※100個程序員的秘密
※Python學習沒那麼難!只要找對方法,只花五分鐘就可以學會排序演算法!
TAG:Python |