正文:
本文展示一些高級的Python設計結構和它們的使用方法。在日常工作中,你可以根據需要選擇合適的數據結構,例如對快速查找性的要求、對數據一致性的要求或是對索引的要求等,同時也可以將各種數據結構合適地結合在一起,從而生成具有邏輯性并易于理解的數據模型。Python的數據結構從句法上來看非常直觀,并且提供了大量的可選操作。這篇指南嘗試將大部分常用的數據結構知識放到一起,并且提供對其最佳用法的探討。
推導式(Comprehensions)
如果你已經使用了很長時間的Python,那么你至少應該聽說過列表推導(list comprehensions)。這是一種將for循環、if表達式以及賦值語句放到單一語句中的一種方法。換句話說,你能夠通過一個表達式對一個列表做映射或過濾操作。
一個列表推導式包含以下幾個部分:
舉個例子,我們需要從一個輸入列表中將所有大于0的整數平方生成一個新的序列,你也許會這么寫:
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]
很簡單是吧?但是這就會有4行代碼,兩層嵌套外加一個完全不必要的append操作。而如果使用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]
列表推導式被封裝在一個列表中,所以很明顯它能夠立即生成一個新列表。這里只有一個type函數調用而沒有隱式調用lambda函數,列表推導式正是使用了一個常規的迭代器、一個表達式和一個if表達式來控制可選的參數。
另一方面,列表推導也可能會有一些負面效應,那就是整個列表必須一次性加載于內存之中,這對上面舉的例子而言不是問題,甚至擴大若干倍之后也都不是問題。但是總會達到極限,內存總會被用完。
針對上面的問題,生成器(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 # <generator object <genexpr> at 0x00583E18> for item in filtered_and_squared: 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)# <generator object <genexpr> at 0x004E6418> # Option Ifor k in square_generator(0): print k# 1, 16, 100, 4, 9 # Option IIg = list(square_generator(0))print g# [1, 16, 100, 4, 9]
除非特殊的原因,應該經常在代碼中使用生成器表達式。但除非是面對非常大的列表,否則是不會看出明顯區別的。
下例使用zip()函數一次處理兩個或多個列表中的元素:
alist = ['a1', 'a2', 'a3']blist = ['1', '2', '3'] for a, b in zip(alist, blist): print a, b # a1 1# a2 2# a3 3
再來看一個通過兩階列表推導式遍歷目錄的例子:
import osdef 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裝飾器(Decorators)
裝飾器為我們提供了一個增加已有函數或類的功能的有效方法。聽起來是不是很像Java中的面向切面編程(Aspect-Oriented Programming)概念?兩者都很簡單,并且裝飾器有著更為強大的功能。舉個例子,假定你希望在一個函數的入口和退出點做一些特別的操作(比如一些安全、追蹤以及鎖定等操作)就可以使用裝飾器。
裝飾器是一個包裝了另一個函數的特殊函數:主函數被調用,并且其返回值將會被傳給裝飾器,接下來裝飾器將返回一個包裝了主函數的替代函數,程序的其他部分看到的將是這個包裝函數。
def timethis(func): ''' Decorator that reports the execution time. ''' pass @timethisdef countdown(n): while n > 0: n -= 1
語法糖@標識了裝飾器。
好了,讓我們回到剛才的例子。我們將用裝飾器做一些更典型的操作:
import timefrom 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 @timethisdef countdown(n): while n > 0: n -= 1 countdown(100000) # ('countdown', 0.006999969482421875)當你寫下如下代碼時:
@timethisdef countdown(n):
意味著你分開執行了以下步驟:
def countdown(n):...countdown = timethis(countdown)
裝飾器函數中的代碼創建了一個新的函數(正如此例中的wrapper函數),它用 *args 和 **kwargs 接收任意的輸入參數,并且在此函數內調用原函數并且返回其結果。你可以根據自己的需要放置任何額外的代碼(例如本例中的計時操作),新創建的包裝函數將作為結果返回并取代原函數。
@decoratordef function(): print("inside function")當編譯器查看以上代碼時,function()函數將會被編譯,并且函數返回對象將會被傳給裝飾器代碼,裝飾器將會在做完相關操作之后用一個新的函數對象代替原函數。
裝飾器代碼是什么樣的?大部分的例子都是將裝飾器定義為函數,而我發覺將裝飾器定義成類更容易理解其功能,并且這樣更能發揮裝飾器機制的威力。
對裝飾器的類實現唯一要求是它必須能如函數一般使用,也就是說它必須是可調用的。所以,如果想這么做這個類必須實現__call__方法。
這樣的裝飾器應該用來做些什么?它可以做任何事,但通常它用在當你想在一些特殊的地方使用原函數時,但這不是必須的,例如:
class decorator(object): def __init__(self, f): print("inside decorator.__init__()") f() # Prove that function definition has completed def __call__(self): print("inside decorator.__call__()") @decoratordef function(): print("inside function()") print("Finished decorating function()") function() # inside decorator.__init__()# inside function()# Finished decorating function()# inside decorator.__call__()譯者注:
1. 語法糖@decorator相當于function=decorator(function),在此調用decorator的__init__打印“inside decorator.__init__()”
2. 隨后執行f()打印“inside function()”
3. 隨后執行“print(“Finished decorating function()”)”
4. 最后在調用function函數時,由于使用裝飾器包裝,因此執行decorator的__call__打印 “inside decorator.__call__()”。
一個更實際的例子:
def decorator(func): def modify(*args, **kwargs): variable = kwargs.pop('variable', None) print variable x,y=func(*args, **kwargs) return x,y return modify @decoratordef func(a,b): print a**2,b**2 return a**2,b**2 func(a=4, b=5, variable="hi")func(a=4, b=5) # hi# 16 25# None# 16 25上下文管理庫(ContextLib)
contextlib模塊包含了與上下文管理器和with聲明相關的工具。通常如果你想寫一個上下文管理器,則你需要定義一個類包含__enter__方法以及__exit__方法,例如:
import timeclass 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聲明所激活,這個API涉及到兩個方法。
1. __enter__方法,當執行流進入with代碼塊時,__enter__方法將執行。并且它將返回一個可供上下文使用的對象。
2. 當執行流離開with代碼塊時,__exit__方法被調用,它將清理被使用的資源。
利用@contextmanager裝飾器改寫上面那個例子:
from contextlib import contextmanagerimport time @contextmanagerdef 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看上面這個例子,函數中yield之前的所有代碼都類似于上下文管理器中__enter__方法的內容。而yield之后的所有代碼都如__exit__方法的內容。如果執行過程中發生了異常,則會在yield語句觸發。
描述器(Descriptors)
描述器決定了對象屬性是如何被訪問的。描述器的作用是定制當你想引用一個屬性時所發生的操作。
構建描述器的方法是至少定義以下三個方法中的一個。需要注意,下文中的instance是包含被訪問屬性的對象實例,而owner則是被描述器修辭的類。
__get__(self, instance, owner) 主站蜘蛛池模板: 阳高县| 金寨县| 苍溪县| 侯马市| 铅山县| 钟祥市| 鹤岗市| 齐河县| 广丰县| 汉寿县| 疏勒县| 通辽市| 临西县| 措美县| 遂溪县| 永泰县| 双鸭山市| 本溪| 揭阳市| 土默特左旗| 清镇市| 岚皋县| 肥乡县| 康定县| 高雄市| 金乡县| 金昌市| 建湖县| 乐昌市| 江华| 汉川市| 玉田县| 饶阳县| 华阴市| 钟祥市| 永德县| 张北县| 鸡泽县| 山阳县| 钟祥市| 西林县|