自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

掌握這九個(gè)技巧,讓Python代碼快如閃電

開發(fā) 前端
當(dāng)工程師們說(shuō)到Python時(shí),默認(rèn)情況下指的是CPython。因?yàn)镃Python是Python語(yǔ)言的默認(rèn)實(shí)現(xiàn),也是使用最廣泛的實(shí)現(xiàn)。

前言

這種觀點(diǎn)在關(guān)于編程語(yǔ)言的討論中經(jīng)常出現(xiàn),經(jīng)常掩蓋了Python的眾多優(yōu)點(diǎn)。

事實(shí)是,如果能以Pythonic的方式編寫Python代碼,它是很快的。

細(xì)節(jié)決定成敗。經(jīng)驗(yàn)豐富的Python開發(fā)者掌握了一系列微妙而強(qiáng)大的技巧,可以顯著提高代碼的性能。

這些技巧乍看之下似乎微不足道,但它們可以帶來(lái)效率的大幅提升。讓我們深入了解其中的9種方法,改變編寫和優(yōu)化Python代碼的方式。

1. 更快的字符串連接:巧妙選擇“join()”或“+”

如果有大量字符串等待處理,字符串連接將成為Python程序的瓶頸。

基本上,Python有兩種字符串連接的方式:

  1. 使用join()函數(shù)將一組字符串合并為一個(gè)字符串。
  2. 使用+或+=符號(hào)將每個(gè)單獨(dú)的字符串添加到一個(gè)字符串中。

那么哪種方式更快呢?

現(xiàn)在,讓我們定義3個(gè)不同的函數(shù)來(lái)連接相同的字符串:

mylist = ["Yang", "Zhou", "is", "writing"]


# 使用'+'
def concat_plus():
    result = ""
    for word in mylist:
        result += word + " "
    return result


# 使用'join()'
def concat_join():
    return " ".join(mylist)


# 直接連接而不使用列表
def concat_directly():
    return "Yang" + "Zhou" + "is" + "writing"

根據(jù)你的第一印象,你認(rèn)為哪個(gè)函數(shù)最快,哪個(gè)最慢?

真正的結(jié)果可能會(huì)讓你驚訝:

import timeit

print(timeit.timeit(concat_plus, number=10000))
# 0.002738415962085128
print(timeit.timeit(concat_join, number=10000))
# 0.0008482920238748193
print(timeit.timeit(concat_directly, number=10000))
# 0.00021425005979835987

如上所示,對(duì)于連接一組字符串,join()方法比在for循環(huán)中逐個(gè)添加字符串更快。

原因很簡(jiǎn)單。一方面,字符串在Python中是不可變的數(shù)據(jù),每次+=操作都會(huì)創(chuàng)建一個(gè)新字符串并復(fù)制舊字符串,這在計(jì)算上成本是昂貴的。

另一方面,.join()方法專門針對(duì)連接一系列字符串進(jìn)行了優(yōu)化。它會(huì)預(yù)先計(jì)算出所生成字符串的大小,然后一次性創(chuàng)建它。因此,它避免了循環(huán)中+=操作帶來(lái)的開銷,從而使速度更快。

然而,在我們的測(cè)試中,速度最快的函數(shù)是直接連接字符串文字。它的高速度是由于:

  • Python解釋器可以在編譯時(shí)優(yōu)化字符串文字的連接,將它們轉(zhuǎn)換為一個(gè)單獨(dú)的字符串文字。這里不涉及循環(huán)迭代或函數(shù)調(diào)用,因此是一種非常高效的操作。
  • 由于所有字符串在編譯時(shí)都是已知的,Python可以非??焖俚貓?zhí)行此操作,比在循環(huán)中進(jìn)行的運(yùn)行時(shí)連接或經(jīng)過(guò)優(yōu)化的.join()方法要快得多。

總之,如果需要連接一組字符串,請(qǐng)選擇join()而不是+=。如果想要直接連接字符串,只需使用+即可。

2. 更快的列表創(chuàng)建:使用“[]”而不是“l(fā)ist()”

創(chuàng)建列表并不是很難的事情。常見的兩種方式是:

  1. 使用list()函數(shù)。
  2. 直接使用[]。

讓我們使用一個(gè)簡(jiǎn)單的代碼片段來(lái)測(cè)試它們的性能:

import timeit

print(timeit.timeit('[]', number=10 ** 7))
# 0.1368238340364769
print(timeit.timeit(list, number=10 ** 7))
# 0.2958830420393497

結(jié)果顯示,執(zhí)行l(wèi)ist()函數(shù)比直接使用[]要慢。

這是因?yàn)閇]是一種字面量語(yǔ)法,而list()是一個(gè)構(gòu)造函數(shù)調(diào)用。調(diào)用函數(shù)無(wú)疑需要額外的時(shí)間。

從同樣的邏輯出發(fā),在創(chuàng)建字典時(shí),我們也應(yīng)該使用{}而不是dict()。

3. 更快的成員測(cè)試:使用集合而不是列表

成員測(cè)試操作的性能在很大程度上依賴于底層數(shù)據(jù)結(jié)構(gòu):

import timeit

large_dataset = range(100000)
search_element = 2077

large_list = list(large_dataset)
large_set = set(large_dataset)


def list_membership_test():
    return search_element in large_list


def set_membership_test():
    return search_element in large_set


print(timeit.timeit(list_membership_test, number=1000))
# 0.01112208398990333
print(timeit.timeit(set_membership_test, number=1000))
# 3.27499583363533e-05

正如上述代碼所示,使用集合進(jìn)行成員測(cè)試比使用列表更快。

為什么會(huì)這樣呢?

  • 在Python的列表中,成員測(cè)試(列表中的元素)是通過(guò)迭代每個(gè)元素直到找到所需的元素或達(dá)到列表的末尾來(lái)完成。因此,此操作的時(shí)間復(fù)雜度為O(n)。
  • Python中的集合實(shí)現(xiàn)形式為哈希表。在進(jìn)行成員檢查(集合中的元素)時(shí),Python使用哈希機(jī)制,其平均時(shí)間復(fù)雜度為O(1)。

這里的關(guān)鍵在于:在編寫程序時(shí)要仔細(xì)考慮底層數(shù)據(jù)結(jié)構(gòu)。正確利用合適的數(shù)據(jù)結(jié)構(gòu)可以顯著加快代碼的運(yùn)行速度。

4. 更快的數(shù)據(jù)生成:使用推導(dǎo)式而不是for循環(huán)

Python中有四種推導(dǎo)式類型:列表推導(dǎo)式、字典推導(dǎo)式、集合推導(dǎo)式和生成器推導(dǎo)式。它們不僅為創(chuàng)建相對(duì)數(shù)據(jù)結(jié)構(gòu)提供了更簡(jiǎn)潔的語(yǔ)法,而且比使用for循環(huán)更高效,因?yàn)樗鼈冊(cè)赑ython的C實(shí)現(xiàn)中進(jìn)行了優(yōu)化。

import timeit


def generate_squares_for_loop():
    squares = []
    for i in range(1000):
        squares.append(i * i)
    return squares


def generate_squares_comprehension():
    return [i * i for i in range(1000)]


print(timeit.timeit(generate_squares_for_loop, number=10000))
# 0.2797503340989351
print(timeit.timeit(generate_squares_comprehension, number=10000))
# 0.2364629579242319

上述代碼是列表推導(dǎo)式和for循環(huán)之間的簡(jiǎn)單速度比較。結(jié)果顯示,列表推導(dǎo)式更快。

5. 更快的循環(huán):優(yōu)先使用局部變量

在Python中,訪問(wèn)局部變量比訪問(wèn)全局變量或?qū)ο蟮膶傩愿臁?/p>

以下是一個(gè)實(shí)例來(lái)證明這一點(diǎn):

import timeit


class Example:
    def __init__(self):
        self.value = 0


obj = Example()


def test_dot_notation():
    for _ in range(1000):
        obj.value += 1


def test_local_variable():
    value = obj.value
    for _ in range(1000):
        value += 1
    obj.value = value


print(timeit.timeit(test_dot_notation, number=1000))
# 0.036605041939765215
print(timeit.timeit(test_local_variable, number=1000))
# 0.024470250005833805

這就是Python的工作原理。直觀地說(shuō),當(dāng)一個(gè)函數(shù)被編譯時(shí),其中的局部變量是已知的,但其他外部變量需要時(shí)間來(lái)檢索。

這可能是一個(gè)小問(wèn)題,但是當(dāng)處理大量數(shù)據(jù)時(shí),我們可以利用它來(lái)優(yōu)化我們的代碼。

6. 更快的執(zhí)行速度:優(yōu)先使用內(nèi)置模塊和庫(kù)

當(dāng)工程師們說(shuō)到Python時(shí),默認(rèn)情況下指的是CPython。因?yàn)镃Python是Python語(yǔ)言的默認(rèn)實(shí)現(xiàn),也是使用最廣泛的實(shí)現(xiàn)。

鑒于它的大部分內(nèi)置模塊和庫(kù)都是用C語(yǔ)言編寫的,而C語(yǔ)言是一種更快且更底層的語(yǔ)言,因此我們應(yīng)該利用這些內(nèi)置模塊和庫(kù),避免重復(fù)勞動(dòng)。

import timeit
import random
from collections import Counter


def count_frequency_custom(lst):
    frequency = {}
    for item in lst:
        if item in frequency:
            frequency[item] += 1
        else:
            frequency[item] = 1
    return frequency


def count_frequency_builtin(lst):
    return Counter(lst)


large_list = [random.randint(0, 100) for _ in range(1000)]

print(timeit.timeit(lambda: count_frequency_custom(large_list), number=100))
# 0.005160166998393834
print(timeit.timeit(lambda: count_frequency_builtin(large_list), number=100))
# 0.002444291952997446

上面的程序比較了兩種統(tǒng)計(jì)列表中元素頻率的方法??梢钥吹?,利用collections模塊中內(nèi)置的Counter函數(shù)比自己編寫的for循環(huán)更快、更簡(jiǎn)潔、更好。

7. 更快的函數(shù)調(diào)用:利用緩存裝飾器輕松實(shí)現(xiàn)記憶化

緩存是一種常用的技術(shù),用于避免重復(fù)計(jì)算并加快程序的運(yùn)行速度。

幸運(yùn)的是,在大多數(shù)情況下,我們不需要自己編寫緩存處理代碼,因?yàn)镻ython為此提供了一個(gè)開箱即用的裝飾器來(lái)實(shí)現(xiàn)這個(gè)目的——@functools.cache。

例如,下面的代碼將執(zhí)行兩個(gè)生成斐波那契數(shù)的函數(shù),一個(gè)有緩存裝飾器,而另一個(gè)沒有:

import timeit
import functools


def fibonacci(n):
    if n in (0, 1):
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)


@functools.cache
def fibonacci_cached(n):
    if n in (0, 1):
        return n
    return fibonacci_cached(n - 1) + fibonacci_cached(n - 2)


# 測(cè)試每個(gè)函數(shù)的執(zhí)行時(shí)間
print(timeit.timeit(lambda: fibonacci(30), number=1))
# 0.09499712497927248
print(timeit.timeit(lambda: fibonacci_cached(30), number=1))
# 6.458023563027382e-06

結(jié)果證明了@functools.cache裝飾器是如何使我們的代碼變得更快的。

基本的fibonacci函數(shù)效率較低,因?yàn)樵谟?jì)算fibonacci(30)結(jié)果的過(guò)程中,它會(huì)多次重新計(jì)算相同的斐波那契數(shù)。

而使用緩存的版本要快得多,因?yàn)樗彺媪酥暗挠?jì)算結(jié)果。因此,它只計(jì)算每個(gè)斐波那契數(shù)一次,并且對(duì)于相同的參數(shù)再次調(diào)用時(shí)會(huì)從緩存中獲取結(jié)果。

僅僅添加一個(gè)內(nèi)置的裝飾器就可以帶來(lái)如此大的改進(jìn),這就是Pythonic的意義所在。??

8. 更快的無(wú)限循環(huán):優(yōu)先使用"while 1"而不是"while True"

要?jiǎng)?chuàng)建一個(gè)無(wú)限的while循環(huán),我們可以使用while True或while 1。

它們的性能差異通??梢院雎圆挥?jì)。但是,了解while 1稍微快一些還是很有趣的。

這源于1是一個(gè)字面常量,而True是Python全局作用域中需要查找的一個(gè)全局名稱,因此需要一點(diǎn)點(diǎn)額外開銷。

讓我們?cè)诖a片段中進(jìn)一步比較這兩種方式的真實(shí)性能:

import timeit


def loop_with_true():
    i = 0
    while True:
        if i >= 1000:
            break
        i += 1


def loop_with_one():
    i = 0
    while 1:
        if i >= 1000:
            break
        i += 1


print(timeit.timeit(loop_with_true, number=10000))
# 0.1733035419601947
print(timeit.timeit(loop_with_one, number=10000))
# 0.16412191605195403

正如所看到的,while 1的速度確實(shí)稍快一些。

然而,現(xiàn)代的Python解釋器(如CPython)已經(jīng)過(guò)高度優(yōu)化,這種差異通常是微不足道的。所以不需要擔(dān)心這種可忽略的差異。更不用說(shuō)while True比while 1更易讀了。

9. 更快的啟動(dòng):巧妙地導(dǎo)入Python模塊

在Python腳本的頂部導(dǎo)入所有模塊似乎是很自然的。

實(shí)際上,我們并不需要這樣做。

而且,如果一個(gè)模塊太大,在需要時(shí)導(dǎo)入它是一個(gè)更好的選擇。

def my_function():
    import heavy_module
    # 函數(shù)的其余部分

以上代碼中,heavy_module是在函數(shù)內(nèi)部導(dǎo)入的。這是一種“延遲加載”的思想,即延遲到在調(diào)用my_function時(shí)才進(jìn)行導(dǎo)入。

這種方法的好處是,如果在執(zhí)行腳本的過(guò)程中從未調(diào)用過(guò)my_function,那么heavy_module就不會(huì)被加載,從而節(jié)省資源并減少腳本的啟動(dòng)時(shí)間。

結(jié)語(yǔ)

綜上所述,就是9個(gè)優(yōu)化Python代碼性能的實(shí)用技巧,但在實(shí)際應(yīng)用時(shí)需要根據(jù)具體情況進(jìn)行權(quán)衡和調(diào)整。通過(guò)綜合考慮代碼的性能、可讀性和可維護(hù)性,進(jìn)而編寫出高效且易于理解的Python代碼。

責(zé)任編輯:武曉燕 來(lái)源: Python學(xué)研大本營(yíng)
相關(guān)推薦

2019-09-09 16:30:42

Redis架構(gòu)數(shù)據(jù)庫(kù)

2025-04-08 08:10:00

C#代碼編程

2024-08-19 08:54:02

2019-12-25 14:19:21

Python編程語(yǔ)言Java

2017-08-14 10:52:17

小米MIUIMIUI9

2023-08-11 07:20:04

開源工具項(xiàng)目

2025-02-17 08:50:00

CSS代碼JavaScript

2020-05-21 21:36:54

Windows 10Windows 7Windows

2024-01-08 17:09:07

Python解釋器CPython

2024-10-08 10:24:41

Python編程語(yǔ)言

2016-12-07 08:36:58

2023-12-06 12:52:00

Python庫(kù)

2025-04-02 09:30:00

2025-01-10 08:38:16

2020-07-07 14:35:41

Python數(shù)據(jù)分析命令

2025-03-19 07:37:54

2024-04-29 06:50:45

Python代碼運(yùn)行

2024-09-18 05:00:00

Python代碼

2017-08-29 16:25:21

數(shù)據(jù)庫(kù)GPU數(shù)據(jù)存儲(chǔ)

2022-03-03 09:10:24

css隱藏滾動(dòng)條前端開發(fā)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)