Python編程精進:深入理解核心概念

Python現在是世界上最流行的語言之一。憑借其多功能性、易于理解的語法和強大的社區支持,它已成為那些剛開始編程之旅的人的首選語言。

與其他面向初學者的編程語言不同,Python之所以真正強大,是因為它能做的太多了。就像JavaScript因其在任何地方都能使用的能力而臭名昭著一樣,Python也可以用于:

  • 網站開發(后端)
  • 數據分析和可視化
  • 人工智能和機器學習
  • 科學計算
  • 數值模擬
  • 自動化和腳本編寫
  • 游戲開發

但是,僅僅學習Python和掌握它之間是有區別的。

如果你想自成為一名高級開發者,并且Python是你的首選編程語言,你需要了解這些概念并深入理解他們。

1. 面向對象編程 (OOP)

面向對象編程可能是你在編程旅程中需要學習的最重要的概念之一。盡管Python不像Java那樣是一個嚴格的基于類的編程語言,但你確實需要了解良好的OOP概念和最佳實踐,才能在現實世界項目中做出貢獻。

面向對象編程不僅僅是創建類和對象,它是關于以一種在現實世界中有意義的方式構建你的代碼。

2. 一等函數和高階函數

在Python中,函數被視為一等公民。這意味著,像任何其他對象(例如:整數,字符串或浮點數)一樣,函數也可以作為其他函數參數傳遞,可以從另一個函數返回。

高階函數是一個可以接收一個或多個函數作為參數,并返回一個函數作為結果的函數。

正確使用高階函數,可以:

  1. 增加代碼可重用性:高階函數通過允許開發人員創建可以操作其他函數的通用函數,促進代碼重用。這導致代碼更干凈,更易于維護。
  2. 函數式編程技術:理解高階函數對于利用Python的函數式編程能力至關重要,例如使用map()filter()reduce()。這些內置函數允許簡潔和富有表現力的數據操作。
  3. 靈活性和抽象:通過使用高階函數,開發人員可以抽象復雜的操作,使他們的代碼更靈活,更容易適應。例如,傳遞不同的函數作為參數可以在不修改核心邏輯的情況下改變行為。

代碼示例:

def apply_operation(operation, x, y):
    return operation(x, y)

def add(x, y):
    return x + y

def multiply(x, y):
    return x * y

result_add = apply_operation(add, 3, 4) 
result_multiply = apply_operation(multiply, 3, 4) 

print(result_add)       
print(result_multiply)  

3. 閉包

與其他編程語言(例如JavaScript)類似,Python中的閉包允許函數“記住”并訪問其封閉范圍中的變量,即使外部函數已經執行完畢。

這是教科書定義,在我看來,可以這樣理解:

當以下條件滿足時,會創建閉包:

  1. 在外部函數中定義了一個嵌套函數。
  2. 嵌套函數引用了外部函數范圍中的變量。
  3. 外部函數返回了嵌套函數。

這允許嵌套函數“封閉”它需要的變量,保留它們的狀態。

示例:

def outer_function(x):
    def inner_function(y):
        return x + y
    return inner_function

closure = outer_function(10)
result = closure(5)
print(result)  

在上面的例子中,當你調用outer_function時,它返回了一個inner_function對象。注意內部函數將x加到你傳遞給它的任何數字上。

現在當你調用閉包(不過是inner_function對象)時,你會得到最終值15

現在它可能看起來很無聊,為什么要那樣做?有什么意義?

但這確實是一個非常重要的概念,它在很多現實世界的地方都有用。

  1. 創建事件處理程序
  2. 通過數據封裝維護狀態
  3. 實現裝飾器(閉包是裝飾器背后的機制)

4. 裝飾器

如果你在任何現實世界項目中使用過Python,你可能會知道Python裝飾器的強大之處。深入理解Python裝飾器在我看來對于任何中級或高級Python開發者都是必不可少的。

簡單來說,裝飾器是一個接受另一個函數作為參數,擴展其行為并返回一個新函數的函數。這允許在不修改原始函數本身的情況下動態添加額外的功能。

Python中創建裝飾器的基本示例:

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("Before the function call")
        result = func(*args, **kwargs)  # 執行被裝飾的函數
        print("After the function call")
        return result
    return wrapper

# 使用裝飾器
@my_decorator
def say_hello():
    print("Hello, world!")

say_hello()

輸出

Before the function call
Hello, world!
After the function call

如果要創建帶參數的裝飾器,需要在原始裝飾器外層再嵌套一層函數。

def repeat(times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(times):
                func(*args, **kwargs)
        return wrapper
    return decorator

@repeat(3)  # 傳遞參數
def greet():
    print("Hello!")

greet()

多個裝飾器可以按順序應用于同一個函數,裝飾器從里到外依次應用。示例如下:

def uppercase(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result.upper()
    return wrapper

def exclaim(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result + "!!!"
    return wrapper

@exclaim
@uppercase
def greet(name):
    return f"Hello, {name}"

print(greet("Alice"))

5. 迭代器,可迭代對象和生成器

在Python中,迭代器和可迭代對象是兩個不同但相關的工具,它們共同幫助你一次迭代一個數據流或容器中的數據。迭代器控制迭代過程,而可迭代對象通常持有你想要一次迭代一個值的數據。

在Python中,迭代器和可迭代對象是基礎概念,學習如何通過實現迭代器模式創建迭代器對象對于任何Python開發者都是必要的。

迭代器負責兩個主要動作:

  1. 返回來自流或容器的數據一次一個項目
  2. 跟蹤****當前已訪問的項目

迭代協議

任何對象,要被視為迭代器,需要實現以下兩個特殊方法:

  • __iter__():初始化迭代器,必須返回一個迭代器對象
  • __next__():獲取數據流中的下一個迭代器對象

實現迭代器的示例:

class Counter:
    def __init__(self, low, high):
        self.current = low
        self.high = high

    def __iter__(self):
        return self

       def __next__(self):
        if self.current > self.high:
            raise StopIteration
        else:
            self.current += 1
            return self.current - 1

for num in Counter(1, 5):
    print(num)

生成器函數

生成器非常迷人。它們讓你有靈活性將任何函數轉換為迭代器(一個非常簡單的定義)。

Python生成器是一種特殊類型的函數,允許你以簡單高效的方式創建迭代器。

與返回單個值并立即終止的常規函數不同,生成器使用yield關鍵字隨時間產生一系列值,每次產生后暫停執行。

這意味著當你調用一個生成器函數時,它不會立即運行函數體;相反,它返回一個可以迭代以一次產生值的生成器對象。

這種方法在處理大型數據集或數據流時特別有用,因為它允許你按需生成值而不會消耗大量內存。

解釋概念的基本示例:

def count_up_to(n):
    count = 1
    while count <= n:
        yield count
        count += 1

for number in count_up_to(5):
    print(number)

6. 上下文管理器

如果你有任何編程經驗,你知道我們經常需要與外部服務交互。無論是文件還是數據庫。與任何外部服務工作的一個重要部分是正確分配和釋放資源。

如果你剛開始學習任何語言的編程,你會認識到這個模式:

file = open('example.txt', 'w')
try:
    file.write('Hello, World!')
finally:
    file.close()

你打開一個文件,與它交互,然后最重要的是關閉它

這是基本的分配和釋放過程。當涉及到數據庫連接時,你也會做類似的操作。工作完成后關閉連接。

上下文管理器使這個過程變得簡單。上下文管理器是實現了__enter____exit__兩個特殊函數的對象。當你使用with關鍵字進入上下文管理器時,執行分配或__enter__部分,當你退出時,執行釋放或__exit__部分。

所以如果你需要與文件交互,使用上下文管理器,它看起來像這樣:

with open('example.txt', 'w') as file:
    file.write('Hello, World!')

7. 內存管理

Python是一種高級編程語言。這意味著,大部分復雜性都從你這里抽象出來,以便你可以專注于應用程序的業務邏輯。

這些抽象之一是Python的內存管理。Python通過其垃圾收集過程負責分配和釋放內存,所以你不必每次使用后都釋放內存。

但是,你在編程旅程中越是高級,你就越想要控制權,這就是為什么學習Python中的內存管理概念是必要的。這可以幫助你在調試復雜問題時,在查找應用程序中的內存泄漏以及更好地理解幕后發生的事情。

不深入細節,以下是你應該了解的關鍵概念:

1. 內存分配

Python通過一個私有堆來管理內存,這是專門用于Python對象和數據結構的內存部分。Python內存管理器負責分配和釋放這部分內存。

  • 靜態內存:為在運行時不改變的變量分配,通常存儲在棧中。
  • 動態內存:在運行時分配,用于大小可能變化的對象,通常存儲在堆中。

2. 內存池

Python使用內存池系統來有效管理小塊內存。當創建一個對象時,Python檢查池中是否有可用的塊可以容納它。如果沒有,它將從操作系統請求額外的內存。這有助于減少與頻繁分配和釋放小塊內存相關的開銷。

3. 引用計數和垃圾收集

Python使用兩種主要技術來管理內存:

  • 引用計數:每個對象都跟蹤有多少引用指向它。當引用計數降至零時,對象可以被安全地釋放。
  • 垃圾收集:為了處理循環引用(兩個或多個對象相互引用),Python使用垃圾收集器定期檢查未引用的對象并釋放它們的內存。

4. 代際垃圾收集

Python的垃圾收集器根據對象的生命周期將對象分類為幾代:

  • 新生代:用于新創建的對象。
  • 老年代:用于在多次垃圾收集周期中存活的對象。

這種代際方法通過專注于更有可能迅速變得不可達的年輕對象來優化性能。

8. 并發

除非你正在處理永遠不會有超過一個用戶(你)或一次運行一個任務的個人項目,否則你需要考慮并發。

根據字典定義,并發意味著同時運行多個任務。有不同的方式可以做到這一點,不同的并發風味有細微的差別,只有當你深入了解它們的內部工作時才會變得清晰。

不同的風味包括:多線程,多進程和異步編程(asyncio)。

但要記住,只有一個(多進程)真正同時運行進程。

Python并發的關鍵主題:

  1. 線程
    threading模塊是Python運行并發任務的最便捷方式之一。然而,由于GIL(全局解釋器鎖,看下一點),一次只能有一個線程執行Python字節碼,這限制了真正的并行性。盡管如此,線程對于文件處理或網絡請求等I/O密集型任務仍然有效。這里的關鍵概念包括管理鎖,了解GIL的影響,以及正確使用線程池(例如,concurrent.futures.ThreadPoolExecutor)。

  2. 多進程
    與線程不同,multiprocessing模塊允許Python程序通過創建單獨的進程來繞過GIL,每個進程都有自己的解釋器和內存空間。這種方法實現了真正的并行性,非常適合CPU密集型任務。然而,進程間通信和內存使用需要仔細管理,通常需要更深入地了解共享內存、隊列和用于管理工作進程池的multiprocessing.Pool類等主題。

  3. 異步編程
    隨著asyncio和其他異步庫如aiohttpasyncpg的引入,Python現在對異步編程有了強大的支持。這種方法特別適合I/O密集型和高延遲任務,如處理多個網絡請求。理解async/await語法、事件循環和任務調度對于掌握異步代碼至關重要。這種模型與線程和多進程不同,它依賴于非阻塞調用,允許多個任務在單個線程上似乎并行運行。

  4. 并發庫和框架
    對于更高級的使用,Python提供了簡化并發管理的庫,如concurrent.futures用于抽象線程和多進程,以及asyncio用于原生異步支持。像geventTwisted這樣的工具提供了處理高并發網絡應用的替代方法,可能值得探索。

Python并發的挑戰:

Python并發帶來了諸如處理GIL、管理資源爭用(例如,死鎖、競態條件)和調試并發代碼等挑戰。此外,每種并發模型都引入了自己的權衡,要求開發人員根據工作負載是CPU密集型還是I/O密集型來選擇正確的方法。

9. 全局解釋器鎖 (GIL)

軟件工程是一個權衡的游戲。

GIL或全局解釋器鎖是標準Python解釋器(CPython版本)中的一個機制,只允許一個線程一次執行Python字節碼。這意味著即使在多線程Python程序中,也只有一個線程可以在任何給定時刻在解釋器中執行。雖然GIL簡化了內存管理并使CPython更加穩定,但它限制了Python在CPU密集型程序中執行真正的并行性的能力。

但為什么需要GIL呢?

Python的內存管理默認不是線程安全的,所以為了防止這種情況并有效管理內存,CPython版本中引入了GIL。GIL的實現使內存管理變得更簡單,因為它簡化了引用計數,這是Python內存管理過程的核心。但這也確實限制了Python在CPU密集型任務中執行真正的并行性的能力。

與此相關的一些概念。

  1. 理解GIL對并發的影響,無論是I/O密集型還是CPU密集型任務
  2. 線程限制:了解GIL如何影響Python的threading模塊是決定何時使用線程與進程的關鍵。
  3. 多進程:熟悉multiprocessing模塊,它提供不受GIL限制的基于進程的并行性。
  4. Asyncio和非阻塞I/O:對于I/O密集型任務,asyncio模塊提供了一種無需線程即可實現并發的方式,繞過了許多與GIL相關的問題。
  5. 其他Python實現:PyPy、Jython和IronPython是其他解釋器,它們不使用GIL或有不同的實現,可能為特定用例提供性能優勢。

10. 協程和異步編程

作為一種編程范式,異步編程旨在高效使用資源。

想象一下,有一家餐廳的排隊,每個人點菜、付款并接收他們的訂單,然后才從隊列中移動。這是同步執行,即使他們只是在等待,一個任務也會阻塞其他任務的執行。

現在在另一家餐廳,你排隊,點菜并拿到收據,然后移動到另一個隊列等待,這意味著你不阻塞下一個人的排隊。這非常非常表面地,就是我們想要用異步編程做的事情。

你應該了解的異步編程主題(與語言無關的概念):

  1. 理解阻塞和非阻塞任務之間的區別
  2. 事件循環
  3. 回調和Promises/Futures
  4. 協程
  5. 異步上下文中的錯誤處理

在Python中,異步編程通過asyncio庫得到支持。你用async關鍵字定義一個函數,將其轉換為協程,以便它可以在異步上下文中執行。

要執行一個async函數(協程),你通常在另一個異步函數內使用await,或者你可以在一個事件循環內運行它(例如,使用asyncio.run()

作為一個Python開發者,深入理解這些概念將給你帶來巨大的優勢。

本文由mdnice多平臺發布

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容