Python 特有特性
本章學習知識點
-
生成器(yield 關鍵字):節省內存的迭代器
-
迭代器(iter/next):理解可迭代對象與迭代器的區別
-
上下文管理器(with 語句):文件操作、數據庫連接(自動關閉資源)
-
推導式:列表推導式、字典推導式、集合推導式(簡潔高效)
-
優勢與適用場景
特性 核心優勢 適用場景 迭代器 統一遍歷邏輯、惰性計算 自定義遍歷邏輯、封裝複雜迭代規則 生成器 極簡語法、內存極致優化 超大數據集處理、批量生成數據 上下文管理器 資源自動管理、代碼安全簡潔 文件操作、數據庫連接、網絡連接等資源管理 推導式 代碼簡潔、執行高效 快速創建列表/字典/集合、簡單數據過濾轉換
Python 憑藉其簡潔優雅且高效的語法特性,在眾多編程語言中獨樹一幟。其中,生成器、迭代器、上下文管理器和推導式是最具代表性的特有特性,它們不僅簡化了代碼編寫,更在內存優化、資源管理等核心場景中展現出強大優勢。
一、迭代器
在Python中,我們經常使用for循環遍歷列表、字典等數據結構,而支撐這一操作的底層機制就是「迭代器」。迭代器是一種可以逐個返回元素的對象,它實現了「迭代協議」,是高效遍歷數據的基礎。
1.1、核心概念
迭代器(Iterator)一定是可迭代對象(Iterable),但可迭代對象不一定是迭代器,二者是「包含與被包含」的關係,且核心目標都是支持「逐個取值」(遍歷),但設計定位和能力完全不同。
-
核心定義
概念 核心定義 核心功能 可迭代對象 只要實現了 __iter__()方法(或__getitem__()且索引從 0 開始),能被for循環遍歷的對象提供「迭代的數據源」,是遍歷的「原材料」 迭代器 同時實現 __iter__()和__next__()方法,且__iter__()返回自身的對象執行「迭代的具體過程」,逐個產出數據,記錄遍歷位置 -
關鍵區別
維度 可迭代對象(Iterable) 迭代器(Iterator) 核心方法 僅需實現 __iter__()(返回迭代器)必須實現 __iter__()(返回自身)+__next__()(返回下一個值,無值拋StopIteration)狀態性 無狀態(不記錄遍歷位置) 有狀態(記錄當前遍歷到的位置) 一次性 可重複遍歷(每次遍歷生成新迭代器) 一次性遍歷(遍歷完後耗盡,無法重置,需重新生成) 典型例子 列表 [1,2,3]、元組(1,2,3)、字符串、字典、集合列表迭代器( iter([1,2,3]))、生成器、enumerate()結果、文件對象直接取值 不能直接用 next()取值可直接用 next()逐個取值
1.2、迭代器使用
-
核心功能及使用方法
-
可迭代對象:「數據源」的角色
可迭代對象是「能被遍歷的容器 / 數據源」,但它自己不會執行 “逐個取值” 的動作,而是通過
__iter__()方法返回一個迭代器,讓迭代器去完成遍歷。# 列表是可迭代對象(Iterable) lst = [1, 2, 3] # 調用 __iter__() 得到迭代器(Iterator) it = iter(lst) # 等價於 lst.__iter__() -
迭代器:「遍歷執行者」的角色
迭代器是「真正幹活的」—— 通過
__next__()方法逐個產出數據,且會記錄當前遍歷的位置(有狀態),遍歷完後再調用next()會拋出StopIteration異常(標誌遍歷結束)。# 1. 將可迭代對象(列表)轉為迭代器 nums = [1, 2, 3, 4] num_iter = iter(nums) # 轉換為迭代器 # 2. 用 next() 逐個獲取元素 print(next(num_iter)) # 輸出:1(獲取第一個元素) print(next(num_iter)) # 輸出:2(獲取第二個元素) print(next(num_iter)) # 輸出:3 print(next(num_iter)) # 輸出:4 # print(next(num_iter)) # 無元素時拋出 StopIteration 異常 -
for 循環的底層邏輯(串聯二者)
for循環,本質是自動完成了「可迭代對象→迭代器→逐個取值」的過程,自動處理 StopIterationfor x in [1,2,3]: print(x) # 等價於 it = iter([1,2,3]) # 1. 調用可迭代對象的 __iter__() 得到迭代器 while True: try: x = next(it) # 2. 調用迭代器的 __next__() 取值 print(x) except StopIteration: # 3. 捕獲異常,結束循環 break
-
-
自定義迭代器(實現迭代協議)
-
通過在類中實現
__iter__()和__next__()方法,可自定義迭代器,實現複雜的遍歷邏輯。class MyIterator: def __init__(self, start, end): self.current = start # 當前迭代值 self.end = end # 迭代終止值 # 實現 __iter__():返回迭代器自身 def __iter__(self): return self # 實現 __next__():返回下一個元素,無元素時拋異常 def __next__(self): if self.current > self.end: raise StopIteration # 終止迭代的信號 temp = self.current self.current += 1 return temp # 使用自定義迭代器 my_iter = MyIterator(1, 5) for num in my_iter: print(num, end=" ") # 輸出:1 2 3 4 5 # 迭代器是一次性的,再次遍歷無結果 for num in my_iter: print(num, end=" ") # 無輸出
-
1.3、迭代器總結
-
核心價值
-
節省內存:迭代器是「惰性取值」(用的時候才生成值),比如生成器(一種迭代器)遍歷百萬級數據時,不會像列表一樣一次性加載所有數據到內存,而是取一個生成一個;
# 生成器(迭代器):惰性取值,內存佔用極低 gen = (x for x in range(1000000)) print(next(gen)) # 0(僅生成第一個值) -
統一遍歷接口:不管是列表、字典、文件等不同類型的可迭代對象,都能通過「轉迭代器→調 next」的方式遍歷,實現了遍歷邏輯的統一;
-
狀態保持:迭代器的 “狀態性” 讓遍歷過程能持續推進,而可迭代對象的 “無狀態性” 讓它可以重複被遍歷(每次生成新迭代器)。
-
迭代器的價值彙總: 在於「節省內存」(無需一次性加載所有數據)和「統一遍歷接口」(所有迭代器都能用
next()/for遍歷)。
-
-
總結
- 可迭代對象:是 “數據源”,回答「遍歷什麼」的問題,無狀態、可重複遍歷,通過
__iter__()交出迭代器; - 迭代器:是 “遍歷器”,回答「怎麼遍歷」的問題,有狀態、一次性遍歷,通過
__next__()逐個產出數據。 - 簡單記: 可迭代對象是 “倉庫”,迭代器是 “倉庫管理員”—— 倉庫提供數據,管理員負責逐個拿出數據,且管理員拿完一次就沒法回頭,要重新拿得換個新管理員(重新生成迭代器)。
- 可迭代對象:是 “數據源”,回答「遍歷什麼」的問題,無狀態、可重複遍歷,通過
二、生成器
生成器是 Python 中迭代器的一種特殊實現,核心特點是「惰性求值」(按需生成數據),既能大幅節省內存,又能簡化迭代邏輯。它是處理大規模數據、無限序列、分步計算的最優方案之一。
-
生成器的核心定位
生成器本質是:自動實現了
__iter__()和__next__()方法的迭代器,無需手動編寫這兩個方法,只需在函數中使用yield關鍵字即可,且遵循迭代器的「一次性、有狀態、惰性取值」規則。- 與普通迭代器的區別:生成器由 Python 解釋器自動生成迭代器協議的實現,代碼極簡;
- 與列表 / 元組的區別:列表一次性加載所有數據到內存,生成器僅在調用
next()時生成下一個值,內存佔用幾乎可忽略。
2.1、生成器的兩種創建方式
-
生成器表達式(簡易版)
-
語法和列表推導式幾乎一致,只是把
[]換成(),適合簡單的迭代邏輯。 -
語法:
gen = (表達式 for 變量 in 可迭代對象 if 條件)# 列表推導式(一次性生成所有數據,佔內存) lst = [x*2 for x in range(5)] print(lst) # [0, 2, 4, 6, 8] # 生成器表達式(惰性求值,僅創建生成器對象,未生成任何值) gen = (x*2 for x in range(5)) print(gen) # <generator object <genexpr> at 0x10xxxxx> # 調用 next() 才生成值(有狀態,逐個產出) print(next(gen)) # 0 print(next(gen)) # 2 print(next(gen)) # 4
-
-
生成器函數(yield 關鍵字)
-
在函數中使用
yield關鍵字(替代return),函數調用後不會執行函數體,而是返回一個生成器對象;每次調用next()時,函數執行到yield處暫停並返回值,下次調用從暫停處繼續執行。 -
核心規則:
yield:暫停函數執行,返回當前值,保留函數的變量狀態;next()/send():喚醒函數,繼續執行到下一個yield;- 函數執行完(無更多
yield),調用next()拋出StopIteration。
-
示例
# 定義生成器函數(含 yield) def my_generator(): print("開始執行...") yield 1 # 第一次 next() 執行到這裏,返回1,暫停 print("繼續執行...") yield 2 # 第二次 next() 從這裏繼續,返回2,暫停 print("執行結束...") # 第三次 next() 執行完,無 yield,拋異常 # 調用函數 → 不執行函數體,返回生成器對象 gen = my_generator() print(gen) # <generator object my_generator at 0x10xxxxx> # 逐個取值(觸發函數執行) print(next(gen)) # 打印「開始執行...」,返回 1 print(next(gen)) # 打印「繼續執行...」,返回 2 print(next(gen)) # 打印「執行結束...」,拋出 StopIteration ############################################################# # 定義生成器函數(生成1~n的奇數) def odd_generator(n): current = 1 while current <= n: yield current # 返回current,並暫停函數 current += 2 # 調用生成器函數:返回生成器對象(不執行函數體) odd_gen = odd_generator(10) # 遍歷生成器(惰性計算) for num in odd_gen: print(num, end=" ") # 輸出:1 3 5 7 9 # 生成器是一次性的,再次遍歷無結果 for num in odd_gen: print(num, end=" ") # 無輸出 # 用 next() 手動獲取元素 odd_gen2 = odd_generator(5) print(next(odd_gen2)) # 輸出:1(執行到yield暫停) print(next(odd_gen2)) # 輸出:3(從暫停處繼續執行) print(next(odd_gen2)) # 輸出:5 # print(next(odd_gen2)) # 拋 StopIteration 異常
-
2.2、生成器特性
-
惰性求值
-
生成器不會提前生成所有數據,只有調用
next()、for循環、list()等觸發取值時,才會執行到yield並生成值; -
優勢:處理百萬 / 千萬級數據、無限序列時,內存佔用始終極低(僅保存當前狀態)。
-
示例:無限序列生成器
def infinite_num(): n = 0 while True: yield n n += 1 gen = infinite_num() print(next(gen)) # 0 print(next(gen)) # 1 print(next(gen)) # 2 # 永遠不會耗盡,按需取值,不佔內存
-
-
有狀態且一次性
-
生成器會記錄當前執行的位置(狀態),每次取值從暫停處繼續,無法重置;
-
遍歷完成後(無更多
yield),再次調用next()會拋出StopIteration,無法再次取值(需重新創建生成器)。 -
示例: 一次性特性
gen = (x for x in range(2)) print(list(gen)) # [0, 1](觸發全部取值) print(list(gen)) # [](已耗盡,無值可取)拋出異常 StopIteration
-
-
可暫停 / 恢復(yield 的特性)
-
yield是生成器的 “暫停鍵”,next()是 “恢復鍵”,這讓生成器可以實現「分步執行」,比如:- 分步處理複雜計算;
- 實現協程(簡單版)。
-
示例:分步計算
def step_calc(): x = yield 1 # 第一次返回1,暫停 y = yield x + 2 # 第二次接收send的值,計算後返回,暫停 yield y * 3 # 第三次返回最終結果 gen = step_calc() print(next(gen)) # 1(第一步:返回初始值,暫停) print(gen.send(3)) # 5(第二步:x=3,返回3+2=5,暫停) print(gen.send(5)) # 15(第三步:y=5,返回5*3=15)-
參數説明
調用方式 作用 next(gen)喚醒生成器,執行到下一個 yield,不給暫停處的變量傳值(賦值 None)gen.send(val)喚醒生成器,將 val傳給暫停處的變量,再執行到下一個yield第一次調用 必須用 next(gen)或gen.send(None)(否則報錯,因為還未暫停在 yield 處)
-
-
-
支持 send () 傳值(雙向通信)
-
生成器不僅能返回值,還能通過
send(value)向暫停的yield位置傳入值(替代next()),實現「雙向通信」:gen.send(None)等價於next(gen)(第一次必須傳 None,因為還未暫停在yield處);gen.send(value):喚醒生成器,將value傳給當前yield左側的變量,然後執行到下一個yield。
def chat(): while True: msg = yield "請輸入消息:" # 接收外部傳入的消息 if msg == "exit": yield "結束對話" break yield f"你輸入了:{msg}" gen = chat() print(next(gen)) # 請輸入消息:(初始喚醒) print(gen.send("hello")) # 你輸入了:hello print(next(gen)) # 請輸入消息:(繼續喚醒) # 如果輸入 print(gen.send("hello")) 也是喚醒操作 print(gen.send("exit")) # 結束對話
-
2.3、生成器的常用場景
-
處理大規模數據:替代列表推導式,避免一次性加載所有數據到內存。
# 處理1000萬條數據,生成器僅佔少量內存 gen = (x for x in range(10000000) if x % 2 == 0) for num in gen: if num > 100: break print(num) -
讀取大文件(逐行讀取)
Python 文件對象本身是迭代器,但生成器可封裝更復雜的讀取邏輯(比如按行處理、過濾)。
def read_big_file(file_path): with open(file_path, "r") as f: for line in f: # 文件對象逐行迭代,不加載全部內容 yield line.strip() # 處理後返回 gen = read_big_file("big_file.txt") for line in gen: print(line) -
無限序列生成
比如生成無限整數、斐波那契數列等,按需取值,永不耗盡。
def fib(): a, b = 0, 1 while True: yield a a, b = b, a + b gen = fib() for _ in range(5): print(next(gen)) # 0, 1, 1, 2, 3 -
實戰:生成器處理超大文件
場景:處理10GB的日誌文件,提取包含"ERROR"的行。若用列表讀取,會一次性加載所有內容到內存導致內存溢出;用生成器可逐行讀取,內存佔用恆定。
def error_log_generator(log_file): """生成器:逐行讀取日誌文件,返回包含ERROR的行""" with open(log_file, "r", encoding="utf-8") as f: for line in f: # 文件對象本身就是迭代器,逐行讀取 if "ERROR" in line: yield line.strip() # 返回錯誤行並暫停 # 使用生成器處理超大日誌文件 error_gen = error_log_generator("large_log.log") # 遍歷生成器,處理錯誤行(內存佔用始終很低) for error_line in error_gen: # 處理邏輯:如寫入錯誤日誌文件 with open("error.log", "a", encoding="utf-8") as err_f: err_f.write(error_line + "\n")
2.4、生成器的高級用法
-
生成器的關閉(close ())
手動關閉生成器,後續調用
next()會直接拋出StopIteration,且會執行finally塊(如果有)。def gen_demo(): try: yield 1 yield 2 finally: print("生成器被關閉") gen = gen_demo() print(next(gen)) # 1 gen.close() # 手動關閉 print(next(gen)) # 拋出 StopIteration,且打印「生成器被關閉」 -
生成器推導式 vs 列表推導式
特性 生成器推導式 列表推導式 內存佔用 極低(惰性求值) 高(一次性加載所有數據) 執行速度 首次取值慢(按需生成) 初始化快(一次性生成) 可迭代性 一次性遍歷 可重複遍歷 適用場景 大規模數據、無限序列、分步處理 小規模數據、需重複使用的場景 -
總結
- 核心價值:
- 極簡代碼:無需手動實現迭代器協議,
yield一鍵生成迭代器; - 節省內存:惰性求值,僅在需要時生成數據,適合大規模 / 無限數據;
- 靈活控制:支持暫停 / 恢復、雙向通信,可實現分步執行、簡單協程。
- 極簡代碼:無需手動實現迭代器協議,
- 使用原則
- 簡單迭代邏輯 → 生成器表達式;
- 複雜 / 分步邏輯 → 生成器函數;
- 需重複遍歷 / 小規模數據 → 列表;
- 大規模 / 無限數據 → 生成器。
- 核心價值:
三、上下文管理器(with)
在Python中,操作文件、數據庫連接、網絡連接等資源時,必須確保使用後「正確關閉」(如文件不關閉可能導致數據丟失)。傳統方式用try-finally手動關閉資源,代碼繁瑣;而「上下文管理器」通過 with 語句簡化資源的「獲取 - 使用 - 釋放」流程(比如文件操作、數據庫連接、鎖管理等),避免因異常 / 忘記關閉導致的資源泄露。
-
解決什麼問題?
-
無
with管理文件# 手動管理資源,易出錯 f = open("test.txt", "w") try: f.write("hello") except Exception as e: print("寫入失敗:", e) finally: f.close() # 必須手動關閉,否則文件句柄泄露 -
用
withwith open("test.txt", "w") as f: f.write("hello") # 無需手動close!with會自動關閉文件(無論是否拋異常) -
核心價值:
- 自動釋放資源:無需手動調用
close()/release()等方法; - 異常安全:即使代碼塊內拋出異常,資源仍會被正確釋放;
- 代碼簡化:省去
try/finally模板代碼,邏輯更清晰。
- 自動釋放資源:無需手動調用
-
3.1、用法與原理
-
基礎用法
-
文件操作
# 讀文件 with open("test.txt", "r", encoding="utf-8") as f: content = f.read() print(content) # 退出with塊,文件自動關閉(即使read()拋異常) # 同時管理多個文件 with open("in.txt", "r") as f1, open("out.txt", "w") as f2: f2.write(f1.read()) # 讀取f1,寫入f2 -
鎖管理
import threading lock = threading.Lock() # 自動獲取鎖,執行完釋放鎖 with lock: print("臨界區代碼(線程安全)") -
數據庫連接
import sqlite3 # 自動提交/回滾事務,關閉連接 with sqlite3.connect("test.db") as conn: cursor = conn.cursor() cursor.execute("CREATE TABLE IF NOT EXISTS user (id INT)") conn.commit()
-
-
原理:上下文管理器協議
-
上下文管理器協議
with語句的底層是「上下文管理器協議」,任何實現以下兩個方法的對象,都可以作為上下文管理器:方法 作用 __enter__() 進入 with塊時執行:<br />1. 初始化資源(如打開文件、獲取鎖);<br />2. 返回值會被賦值給as後的變量__exit__(exc_type, exc_val, exc_tb) 退出 with塊時執行:<br />1. 釋放資源(如關閉文件、釋放鎖);<br />2. 處理異常(可選) -
執行流程拆解
with open("test.txt","r", encoding='utf-8') as f: f.read() #### 等價於 ################## cm = open("test.txt") # 1. 創建上下文管理器對象 try: f = cm.__enter__() # 2. 執行__enter__,返回值賦值給f f.read() # 3. 執行with塊內的代碼 except Exception as e: # (type(e), e, e.__traceback__) <-- 這裏是固定的值 cm.__exit__(type(e), e, e.__traceback__) # 4. 執行__exit__,傳入異常信息 raise # 默認重新拋出異常 else: cm.__exit__(None, None, None) # 無異常時也執行__exit__
-
3.2、自定義上下文管理器(兩種方式)
-
類實現(最靈活,支持複雜邏輯)
實現
__enter__和__exit__方法即可,示例:自定義文件管理器(模擬open)class MyFileManager: def __init__(self, file_path, mode="r"): self.file_path = file_path self.mode = mode self.file = None # 進入with塊:打開文件,返回文件對象 def __enter__(self): print("打開文件...") self.file = open(self.file_path, self.mode) return self.file # 賦值給as後的變量 # 退出with塊:關閉文件,處理異常 def __exit__(self, exc_type, exc_val, exc_tb): print("關閉文件...") if self.file: self.file.close() # 可選:處理異常(返回True則抑制異常,False則拋出) if exc_type is not None: print(f"捕獲異常:{exc_type}, {exc_val}") # return True # 註釋掉則拋出異常,打開則抑制 return False # 使用自定義上下文管理器 with MyFileManager("test.txt", "w") as f: f.write("自定義管理器") # 故意拋異常測試 # 1 / 0 # 取消註釋,會觸發__exit__的異常處理 -
裝飾器實現(極簡,適合簡單邏輯)
-
用
contextlib.contextmanager裝飾器,將生成器函數轉為上下文管理器,無需寫類。核心規則:-
生成器中
yield之前的代碼 → 對應__enter__(); -
yield之後的代碼 → 對應__exit__(); -
yield
的值 → 賦值給as` 後的變量。
-
-
示例:簡化版文件管理器
from contextlib import contextmanager @contextmanager def my_file_manager(file_path, mode="r"): # __enter__ 邏輯:打開文件 print("打開文件...") file = open(file_path, mode) try: yield file # 返回文件對象,暫停執行 finally: # __exit__ 邏輯:關閉文件(無論是否拋異常) print("關閉文件...") file.close() # 使用 with my_file_manager("test.txt", "w") as f: f.write("裝飾器實現")
-
-
總結
-
實現方式
場景 推薦實現方式 簡單邏輯(一行搞定) contextlib.contextmanager裝飾器複雜邏輯(帶狀態 / 異常處理) 類實現 __enter__/__exit__內置資源(文件 / 鎖) 直接用 with+ 內置上下文管理器 -
核心點
__enter__負責「獲取資源」,__exit__負責「釋放資源」;with語句本質是try/finally的語法糖,但更簡潔、更安全。
-
日常開發中,優先用
with管理所有「需要顯式關閉的資源」(文件、連接、鎖等),避免資源泄露。
-
四、推導式
推導式是Python中創建列表、字典、集合的「極簡語法」,它將for循環、條件判斷等邏輯濃縮為一行代碼,不僅簡潔易讀,且執行效率比普通for循環更高(底層優化)。
-
數據結構中的生成式跟推導式有啥區別? 沒區別,原來只是口語不一樣
推導式類型 語法格式 別稱(生成式) 產出對象 核心特點 列表推導式 [expr for x in iter if cond]列表生成式 列表(list) 一次性加載所有數據到內存 字典推導式 {k:v for x in iter if cond}字典生成式 字典(dict) 鍵唯一,值可重複 集合推導式 {expr for x in iter if cond}集合生成式 集合(set) 元素去重,無序 生成器表達式 (expr for x in iter if cond)生成器生成式(口語) 生成器(generator) 惰性求值,僅佔少量內存 發現沒有元組推導式,因為()包裹的是生成器表達式,如果硬要就是
tuple(expr for x in iter) -
為什麼會有 “生成式” 這個叫法?
“生成式” 是中文使用者的口語化簡化稱呼:
- 推導式的核心動作是 “推導 / 計算”(Comprehension),強調 “從可迭代對象推導出新容器”;
- 生成式的核心動作是 “生成”,強調 “快速生成一個容器 / 生成器對象”;
- 本質是同一語法的兩種描述角度,比如:
- 列表推導式:從邏輯上 “推導” 出列表(規範);
- 列表生成式:從結果上 “生成” 了列表(口語)。
4.1、列表推導式
-
語法格式:
[表達式 for 變量 in 可迭代對象 if 條件判斷]- 表達式:最終添加到列表的元素(可對變量進行運算、轉換等)。
- if 條件判斷(可選):僅滿足條件的變量才會執行表達式並添加到列表。
-
示例
# 案例1:基礎用法(創建1~10的平方列表) square_list = [x**2 for x in range(1, 11)] print(square_list) # 輸出:[1, 4, 9, 16, 25, 36, 49, 64, 81, 100] # 案例2:帶條件判斷(創建1~20的偶數列表) even_list = [x for x in range(1, 21) if x % 2 == 0] print(even_list) # 輸出:[2, 4, 6, 8, 10, 12, 14, 16, 18, 20] # 案例3:複雜表達式(字符串處理:將列表元素轉為大寫並去重) fruits = ["apple", "banana", "apple", "cherry", "banana"] upper_fruits = [fruit.upper() for fruit in fruits if len(fruit) > 5] print(upper_fruits) # 輸出:['BANANA', 'BANANA', 'CHERRY'] # 案例4:嵌套推導式(二維列表轉一維列表) matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] flatten_list = [x for row in matrix for x in row] print(flatten_list) # 輸出:[1, 2, 3, 4, 5, 6, 7, 8, 9] # 對比:普通for循環(代碼繁瑣,效率低) flatten_list2 = [] for row in matrix: for x in row: flatten_list2.append(x)
4.2、字典推導式
-
語法格式
{鍵表達式: 值表達式 for 變量 in 可迭代對象 if 條件判斷}核心是生成鍵值對,適合從可迭代對象快速構建字典,或對現有字典進行轉換。
-
示例
# 案例1:基礎用法(創建1~5的平方字典) square_dict = {x: x**2 for x in range(1, 6)} print(square_dict) # 輸出:{1: 1, 2: 4, 3: 9, 4: 16, 5: 25} # 案例2:帶條件判斷(篩選鍵為偶數的鍵值對) num_dict = {1: "a", 2: "b", 3: "c", 4: "d", 5: "e"} even_key_dict = {k: v for k, v in num_dict.items() if k % 2 == 0} print(even_key_dict) # 輸出:{2: 'b', 4: 'd'} # 案例3:兩個列表映射為字典 names = ["小明", "小紅", "小李"] ages = [20, 18, 19] name_age_dict = {name: age for name, age in zip(names, ages)} print(name_age_dict) # 輸出:{'小明': 20, '小紅': 18, '小李': 19} # 案例4:字典值轉換(將值轉為大寫) fruit_dict = {"a": "apple", "b": "banana", "c": "cherry"} upper_fruit_dict = {k: v.upper() for k, v in fruit_dict.items()} print(upper_fruit_dict) # 輸出:{'a': 'APPLE', 'b': 'BANANA', 'c': 'CHERRY'}
4.3、集合推導式
-
語法格式
{表達式 for 變量 in 可迭代對象 if 條件判斷}與列表推導式類似,區別是用
()改為{},且生成的集合會自動去重(集合的核心特性)。 -
示例
# 案例1:基礎用法(創建1~10的偶數集合) even_set = {x for x in range(1, 11) if x % 2 == 0} print(even_set) # 輸出:{2, 4, 6, 8, 10} # 案例2:自動去重(從重複列表生成無重集合) duplicate_nums = [1, 2, 2, 3, 3, 3, 4, 5, 5] unique_set = {x for x in duplicate_nums} print(unique_set) # 輸出:{1, 2, 3, 4, 5} # 案例3:字符串處理(提取字符串中的唯一字符並轉為大寫) s = "abracadabra" unique_chars = {c.upper() for c in s} print(unique_chars) # 輸出:{'A', 'B', 'C', 'D', 'R'}
4.4、注意事項
- 避免過度複雜:推導式適合簡潔邏輯,若包含多層嵌套(如3層以上
for循環),建議拆分為普通for循環,保證代碼可讀性。 - 區分生成器表達式:生成器表達式是
(表達式 for ...),返回生成器對象;集合推導式是{表達式 for ...},返回集合對象,不可混淆。 - 效率優勢:推導式的執行效率比普通
for循環高20%~50%(底層用C語言優化),但對於超大數據集,建議用生成器(避免內存溢出)。
4.5、實戰
數據處理流水線場景:處理一個1GB的用户行為日誌文件,需求如下:
- 逐行讀取日誌(內存優化);
- 解析日誌中的用户ID和行為類型;
- 統計各行為類型的用户數量(去重);
- 將統計結果保存為JSON文件(資源自動管理)。
import json
from collections import defaultdict
from contextlib import contextmanager
# 1. 生成器:逐行讀取日誌(內存優化)
def log_generator(log_file):
"""生成器:逐行讀取日誌,返回解析後的用户ID和行為類型"""
with open(log_file, "r", encoding="utf-8") as f:
for line in f: # 文件對象是迭代器,逐行讀取
line = line.strip()
if not line:
continue
# 日誌格式:"2025-11-27 10:00:00 | user_123 | click"
parts = line.split(" | ")
if len(parts) == 3:
user_id = parts[1]
action = parts[2]
yield (user_id, action) # 返回解析結果,暫停
# 2. 上下文管理器:JSON文件保存(自動關閉文件)
@contextmanager
def json_writer(file_path):
"""上下文管理器:寫入JSON文件"""
f = open(file_path, "w", encoding="utf-8")
try:
yield f
finally:
f.close()
# 3. 主邏輯:數據處理流水線
def analyze_user_action(log_file, output_file):
# 用defaultdict存儲各行為的用户集合(自動去重)
action_user = defaultdict(set)
# 遍歷生成器,解析日誌並統計
for user_id, action in log_generator(log_file):
action_user[action].add(user_id) # 集合自動去重
# 整理統計結果:{行為類型: 用户數量}
result = {action: len(users) for action, users in action_user.items()}
# 用上下文管理器保存為JSON
with json_writer(output_file) as f:
json.dump(result, f, ensure_ascii=False, indent=2)
print("統計完成!結果已保存至:", output_file)
return result
# 4. 執行分析(模擬日誌文件)
# 先創建模擬日誌文件(1GB實際文件可直接替換路徑)
with open("user_action.log", "w", encoding="utf-8") as f:
actions = ["click", "view", "purchase", "collect"]
for i in range(1000000): # 100萬條模擬日誌
user_id = f"user_{i%10000}" # 1萬個唯一用户
action = actions[i%4] # 循環行為類型
f.write(f"2025-11-27 10:00:00 | {user_id} | {action}\n")
# 執行分析
result = analyze_user_action("user_action.log", "action_stat.json")
print("統計結果:", result)
五、常見易錯點與避坑指南
- 迭代器重複遍歷問題:迭代器是一次性的,遍歷結束後會耗盡,再次遍歷無結果。解決方案:若需重複遍歷,可將迭代器轉為列表(
list(iter_obj)),但需注意內存佔用。 - 生成器暫停邏輯誤解:生成器函數中
yield後的代碼會在下次調用時執行,而非直接跳過。例如:yield x; x +=1,下次調用會先執行x +=1再返回下一個值。 - 上下文管理器異常處理:
__exit__()方法返回True表示已處理異常,不向外拋出;返回False(默認)則會將異常向外傳遞。自定義時需謹慎處理。 - 推導式過度嵌套:如
[x for row in matrix for col in row for x in col](三維轉一維),代碼可讀性差。解決方案:拆分為普通循環或使用itertools.chain等工具。 - 生成器表達式與元組混淆:
(x for x in range(5))是生成器表達式,返回生成器;tuple(x for x in range(5))才是元組。