您好,登錄后才能下訂單哦!
這篇文章主要講解了“有哪些Python加速技能”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“有哪些Python加速技能”吧!
1. 避免使用全局變量
import mathsize = 10000 for x in range(size): for y in range(size): z = math.sqrt(x) + math.sqrt(y)
許多程序員一開始都會(huì)用Python語(yǔ)言編寫一些簡(jiǎn)單的腳本。編寫腳本時(shí),通常直接使用全局變量,就像上面這段代碼。
但由于全局變量和局部變量的實(shí)現(xiàn)方式不同,全局變量中定義的代碼要比在函數(shù)中定義的函數(shù)運(yùn)行起來慢得多。把腳本語(yǔ)句放入函數(shù)中,通常運(yùn)行速度可提高15%-30%。如下所示:
import mathdef main(): size = 10000 for x in range(size): for y in range(size): z = math.sqrt(x) +math.sqrt(y)main()
2. 避免數(shù)據(jù)重復(fù)
避免無意義的數(shù)據(jù)復(fù)制
def main(): size = 10000 for _ in range(size): value = range(size) value_list = [x for x in value] square_list = [x * x for x invalue_list]main()
這段代碼中,value_list完全沒有必要,這會(huì)創(chuàng)建不必要的數(shù)據(jù)結(jié)構(gòu)或復(fù)制。
def main(): size = 10000 for _ in range(size): value = range(size) square_list = [x * x for x invalue]main()
另一個(gè)原因在于Python的數(shù)據(jù)共享機(jī)制過于偏執(zhí),沒有很好理解或信任內(nèi)存模型,例如濫用copy.deepcopy()函數(shù)。我們可以刪除此類代碼中的復(fù)制操作。
交換值時(shí)無需使用中間變量
def main(): size = 1000000 for _ in range(size): a = 3 b = 5 temp = a a = b b = tempmain()
上述代碼在交換值時(shí)創(chuàng)建了一個(gè)臨時(shí)變量temp。如果沒有中間變量,代碼會(huì)更加簡(jiǎn)潔,運(yùn)行速度也更快。
def main(): size = 1000000 for _ in range(size): a = 3 b = 5 a, bb = b, amain()
使用字符串聯(lián)方法join ,而不是'+'
import string from typing import Listdef concatString(string_list: List[str]) -> str: result = '' for str_i in string_list: result += str_i return resultdef main(): string_list =list(string.ascii_letters * 100) for _ in range(10000): result =concatString(string_list)main()
另一要點(diǎn)是a+b對(duì)字符串進(jìn)行拼接,由于在Python中字符串是不可變的對(duì)象,所以實(shí)際上a和b分別復(fù)制到了應(yīng)用程序的新內(nèi)存空間中。
因此,如果拼接n個(gè)字符串會(huì)產(chǎn)生“ n-1”個(gè)中間結(jié)果,則每個(gè)字符串都會(huì)產(chǎn)生應(yīng)用和復(fù)制內(nèi)存所需的中間結(jié)果,從而嚴(yán)重影響操作效率。
在使用join()串聯(lián)字符串時(shí),首先計(jì)算需要應(yīng)用的總內(nèi)存空間,然后立即申請(qǐng)所需的內(nèi)存,再把每個(gè)字符串元素復(fù)制到內(nèi)存中。
import string from typing import Listdef concatString(string_list: List[str]) -> str: return ''.join(string_list)defmain(): string_list = list(string.ascii_letters* 100) for _ in range(10000): result =concatString(string_list)main()
3. 避免使用以下函數(shù)屬性
避免訪問模塊和函數(shù)屬性
import mathdef computeSqrt(size:int): result = [] for i in range(size): result.append(math.sqrt(i)) return resultdef main(): size = 10000 for _ in range(size): result = computeSqrt(size)main()
use(屬性訪問運(yùn)算符)會(huì)觸發(fā)特定方法,例如getattribute()和getattr(),這些方法將執(zhí)行字典操作,會(huì)產(chǎn)生額外的時(shí)間消耗。
通過使用import語(yǔ)句,可以消除屬性訪問:
from math import sqrtdefcomputeSqrt(size: int): result = [] for i in range(size): result.append(sqrt(i)) return resultdef main(): size = 10000 for _ in range(size): result = computeSqrt(size)main()
前文中我們討論了局部變量可以比全局變量實(shí)現(xiàn)更快查找,對(duì)于經(jīng)常訪問的變量(如sqrt),可以通過更改為局部變量以加快操作速度。
import mathdef computeSqrt(size:int): result = [] sqrt = math.sqrt for i in range(size): result.append(sqrt(i)) return resultdef main(): size = 10000 for _ in range(size): result = computeSqrt(size)main()
避免類屬性訪問
import math from typing import Listclass DemoClass: def __init__(self, value: int): self._value = value def computeSqrt(self, size: int)-> List[float]: result = [] append = result.append sqrt = math.sqrt for _ in range(size): append(sqrt(self._value)) return resultdef main(): size = 10000 for _ in range(size): demo_instance = DemoClass(size) result =demo_instance.computeSqrt(size)main()
避免的原理也適用于類的屬性,并且訪問self._value的速度要比訪問局部變量的速度要慢。通過把需要頻繁訪問的類屬性分配給局部變量,可以提高代碼執(zhí)行速度。
import math from typing import Listclass DemoClass: def __init__(self, value: int): self._value = value def computeSqrt(self, size: int)-> List[float]: result = [] append = result.append sqrt = math.sqrt value = self._value for _ in range(size): append(sqrt(value)) return resultdef main(): size = 10000 for _ in range(size): demo_instance = DemoClass(size) demo_instance.computeSqrt(size)main()
4. 避免不必要的抽象
class DemoClass: def __init__(self, value: int): self.value = value@property def value(self) -> int: return self._value@value.setter def value(self, x: int): self._value = xdef main(): size = 1000000 for i in range(size): demo_instance = DemoClass(size) value = demo_instance.value demo_instance.value = imain()
每當(dāng)使用其他處理層(例如裝飾器、屬性訪問、描述符)封裝代碼時(shí),代碼運(yùn)行的速度也會(huì)變慢。在大多數(shù)情況下,重新檢查是否有必要使用屬性訪問器定義是很有必要的。
使用getter/setter函數(shù)訪問屬性通常是被C/C++程序員遺忘的一種編碼樣式。如果確實(shí)沒有必要,就使用簡(jiǎn)單屬性就好。
class DemoClass: def __init__(self, value: int): self.value = valuedef main(): size = 1000000 for i in range(size): demo_instance = DemoClass(size) value = demo_instance.value demo_instance.value = imain()
5. 選擇合適的數(shù)據(jù)結(jié)構(gòu)
眾所周知,列表是Python中的動(dòng)態(tài)數(shù)組。當(dāng)預(yù)分配的內(nèi)存空間用完時(shí),會(huì)預(yù)分配一定的內(nèi)存空間,然后繼續(xù)向其中添加元素。然后復(fù)制之前的所有原始元素,形成一個(gè)新的內(nèi)存空間,在插入新元素之前銷毀先前的內(nèi)存空間。
因此,如果頻繁添加或刪除,或者添加或刪除的元素?cái)?shù)量太大,列表的效率就會(huì)變低,目前最好使用collections.deque。
此雙端隊(duì)列具有堆棧和隊(duì)列的特性,并且可以在兩端以O(shè)(1)復(fù)雜度執(zhí)行插入和刪除操作。
列表搜索操作非常耗時(shí)。當(dāng)需要頻繁查找某些元素或按順序頻繁訪問這些元素時(shí),保持列表 對(duì)象有序的情況下使用二分法,使用二進(jìn)制搜索以提高搜索效率,但二進(jìn)制搜索僅適用于有序元素。
另一個(gè)常見的要求是找到最小值或最大值。此時(shí),可以使用heapq模塊列出轉(zhuǎn)換為堆的列表,因此獲取最小值的時(shí)間復(fù)雜度為O(1)。
6. 循環(huán)優(yōu)化
使用 for 循環(huán)代替while 循環(huán)
def computeSum(size: int) ->int: sum_ = 0 i = 0 while i < size: sum_ += i i += 1 return sum_def main(): size = 10000 for _ in range(size): sum_ = computeSum(size)main()
要知道,Python中的for循環(huán)要比while循環(huán)快得多。
def computeSum(size: int) ->int: sum_ = 0 for i in range(size): sum_ += i return sum_def main(): size = 10000 for _ in range(size): sum_ = computeSum(size)main()
使用隱式for循環(huán),而不是顯式for循環(huán)
對(duì)于上面的示例,可以進(jìn)一步使用隱式for循環(huán)替換顯式for循環(huán)
def computeSum(size: int) ->int: return sum(range(size))def main(): size = 10000 for _ in range(size): sum = computeSum(size)main()
減少內(nèi)部循環(huán)的計(jì)算
from math import sqrtdef main(): size = 10000 for x in range(size): for y in range(size): z = sqrt(x) + sqrt(y)main()
在上述for循環(huán)中的代碼sqrt(x)中,在訓(xùn)練期間每次都需要進(jìn)行重新計(jì)算,這會(huì)增加時(shí)間消耗。
import mathdef main(): size = 10000for x in range(size): sqrtsqrt_x = sqrt(x) for y in range(size): z = sqrt_x + sqrt(y)main()
7. 使用 numba.jit
繼續(xù)遵循上述示例,并在此基礎(chǔ)上使用numba.jit。Python函數(shù)JIT可以編譯為機(jī)器代碼用以執(zhí)行,這能大大提高了代碼執(zhí)行速度。
import numba@numba.jit def computeSum(size: float) -> int: sum = 0 for i in range(size): sum += i return sumdef main(): size = 10000 for _ in range(size): sum = computeSum(size)main()
8. 代碼優(yōu)化原則
第一個(gè)基本原則就是不要過早優(yōu)化代碼。
許多人一開始編寫代碼時(shí)就致力于性能優(yōu)化,“加快正確程序的速度要比確??焖俪绦虻恼_運(yùn)作容易得多?!眱?yōu)化代碼的前提是確保代碼可以正常工作。過早的優(yōu)化可能會(huì)忽略對(duì)總體性能指標(biāo)的掌握,并且在獲得總體結(jié)果之前不要顛倒順序。
第二個(gè)基本原則是權(quán)衡優(yōu)化代碼的成本。
優(yōu)化代碼是有代價(jià)的,想要解決所有性能問題幾乎不可能。通常面臨的選擇是時(shí)間換空間或空間換時(shí)間,還需要考慮開發(fā)成本。
第三個(gè)原則是不要優(yōu)化無關(guān)緊要的部分。
如果優(yōu)化代碼的每個(gè)部分后,這些變更會(huì)讓代碼變得難以閱讀和理解。如果代碼運(yùn)行緩慢,首先必須找到代碼運(yùn)行緩慢的位置(通常是內(nèi)部循環(huán)),重點(diǎn)優(yōu)化代碼運(yùn)行緩慢的地方。對(duì)于其他位置,時(shí)間的損失影響很小。
感謝各位的閱讀,以上就是“有哪些Python加速技能”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)有哪些Python加速技能這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。