- 點擊上方「中國統計網」訂閱我吧!-
有很多老的Python排序代碼,它們在你創建一個自定義的排序時花費你的時間,但在運行時確實能加速執行排序過程。元素排序的最好方法是儘可能使用鍵(key)和默認的sort()排序方法。例如,考慮下面的代碼:
import operatorsomelist = [(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參數部分的索引,數組進行了排序。類似於利用數字進行排序,這種方法同樣適用於利用字符串排序。
每種程式語言都會強調需要優化循環。當使用Python的時候,你可以依靠大量的技巧使得循環運行得更快。然而,開發者經常漏掉的一個方法是:避免在一個循環中使用點操作。例如,考慮下面的代碼:
lowerlist = ['this', 'is', 'lowercase']upper = str.upperupperlist = []append = upperlist.appendfor word in lowerlist:append(upper(word))print(upperlist)#Output = ['THIS', 'IS', 'LOWERCASE']每一次你調用方法str.upper,Python都會求該方法的值。然而,如果你用一個變量代替求得的值,值就變成了已知的,Python就可以更快地執行任務。優化循環的關鍵,是要減少Python在循環內部執行的工作量,因為Python原生的解釋器在那種情況下,真的會減緩執行的速度。
(注意:優化循環的方法有很多,這只是其中的一個。例如,許多程式設計師都會說,列表推導是在循環中提高執行速度的最好方式。這裡的關鍵是,優化循環是程序取得更高的執行速度的更好方式之一。)
如果每次你創建一個應用程式都是用相同的編碼方法,幾乎肯定會導致一些你的應用程式比它能夠達到的運行效率慢的情況。作為分析過程的一部分,你可以嘗試一些實驗。例如,在一個字典中管理一些元素,你可以採用安全的方法確定元素是否已經存在並更新,或者你可以直接添加元素,然後作為異常處理該元素不存在情況。考慮第一個編碼的例子:
n = 16myDict = {}for i in range(0, n):char = 'abcd'[i%4]if char not in myDict:myDict[char] = 0myDict[char] += 1print(myDict)這段代碼通常會在myDict開始為空時運行得更快。然而,當mydict通常被數據填充(或者至少大部分被充填)時,另一種方法效果更好。
n = 16myDict = {}for i in range(0, n):char = 'abcd'[i%4]try:myDict[char] += 1except KeyError:myDict[char] = 1print(myDict)兩種情況下具有相同的輸出:{『d』: 4, 『c』: 4, 『b』: 4, 『a』: 4}。唯一的不同是這個輸出是如何得到的。跳出固定的思維模式,創造新的編碼技巧,能夠幫助你利用你的應用程式獲得更快的結果。
一個列表推導式包含以下幾個部分:
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
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)能夠很好的解決。生成器表達式不會一次將整個列表加載到內存之中,而是生成一個生成器對象(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
for item in filtered_and_squared:print item這比列表推導效率稍微提高一些,讓我們再一次改造一下代碼:
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)
for k in square_generator(0):print k
g = list(square_generator(0))print g除非特殊的原因,應該經常在代碼中使用生成器表達式。但除非是面對非常大的列表,否則是不會看出明顯區別的。再來看一個通過兩階列表推導式遍歷目錄的例子:
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裝飾器為我們提供了一個增加已有函數或類的功能的有效方法。聽起來是不是很像Java中的面向切面編程(Aspect-Oriented Programming)概念?兩者都很簡單,並且裝飾器有著更為強大的功能。舉個例子,假定你希望在一個函數的入口和退出點做一些特別的操作(比如一些安全、追蹤以及鎖定等操作)就可以使用裝飾器。
裝飾器是一個包裝了另一個函數的特殊函數:主函數被調用,並且其返回值將會被傳給裝飾器,接下來裝飾器將返回一個包裝了主函數的替代函數,程序的其他部分看到的將是這個包裝函數。
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 resultreturn wrapper
@timethisdef countdown(n):while n > 0:n -= 1
countdown(100000)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 = 10000000while n > 0:n -= 1上下文管理器被with聲明所激活,這個API涉及到兩個方法。
1. __enter__方法,當執行流進入with代碼塊時,__enter__方法將執行。並且它將返回一個可供上下文使用的對象。
2. 當執行流離開with代碼塊時,__exit__方法被調用,它將清理被使用的資源。
利用@contextmanager裝飾器改寫上面那個例子:
from contextlib import contextmanagerimport time
@contextmanagerdef demo(label):start = time.time()try:yieldfinally:end = time.time()print('{}: {}'.format(label, end - start))
with demo('counting'):n = 10000000while n > 0:n -= 1看上面這個例子,函數中yield之前的所有代碼都類似於上下文管理器中__enter__方法的內容。而yield之後的所有代碼都如__exit__方法的內容。如果執行過程中發生了異常,則會在yield語句觸發。
描述器決定了對象屬性是如何被訪問的。描述器的作用是定製當你想引用一個屬性時所發生的操作。
構建描述器的方法是至少定義以下三個方法中的一個。需要注意,下文中的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.valuedef __set__(self, instance, value):self.value = float(value)
class Temperature(object):celsius = Celsius()
temp=Temperature()temp.celsius38. 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')]39. Grouping adjacent list items using zip >>> a = [1, 2, 3, 4, 5, 6]
>>>
>>> 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,)]
>>>
>>> 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,)]40. 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)]深圳線下·愛數據社區
課程主題:「如何面試數據分析崗位」
本次活動時間:1月18日 星期六 13:00-17:00
講師介紹:王老師,網際網路公司高級數據分析師,多年數據分析業務經驗,具有豐富的經營分析、用戶消費行為分析等項目經歷。
點擊原文,就分你一個小魚乾