pydantic 可變對象默認值行為詳解及解決方案
本文深入探討 Pydantic 類中使用可變對象(如列表、字典)作為默認值時,可能導致實例間數據共享的問題,并提供解決方案。
讓我們來看一個例子:
from typing import List from pydantic import BaseModel class User(BaseModel): friends: List[int] = [] user1 = User() user1.friends.append(1) print(f"user1.friends: {user1.friends}") # 輸出: user1.friends: [1] user2 = User() print(f"user2.friends: {user2.friends}") # 輸出: user2.friends: []
令人困惑的是,friends 屬性的默認值明明是一個空列表,為什么 user1 和 user2 實例的 friends 屬性卻指向不同的列表?
如果我們不使用 BaseModel,結果會不同:
from typing import List class User: friends: List[int] = [] user1 = User() user1.friends.append(1) print(f"user1.friends: {user1.friends}") # 輸出: user1.friends: [1] user2 = User() print(f"user2.friends: {user2.friends}") # 輸出: user2.friends: [1]
現在 user2.friends 也變成了 [1],這說明問題在于 pydantic.BaseModel。
關鍵在于默認值的創建時機。在不繼承 BaseModel 的情況下,friends: List[int] = [] 只在類定義階段執行一次,所有實例共享同一個列表。 而 BaseModel 為了避免此問題,會在每次實例化時創建新的默認值對象,確保每個實例擁有獨立的屬性,避免了意外的副作用。
解決方案:
為了避免這個問題,應該使用工廠函數或 Field 來創建默認值:
方法一:使用工廠函數
from typing import List from pydantic import BaseModel def default_friends(): return [] class User(BaseModel): friends: List[int] = default_friends() user1 = User() user1.friends.append(1) print(f"user1.friends: {user1.friends}") # 輸出: user1.friends: [1] user2 = User() print(f"user2.friends: {user2.friends}") # 輸出: user2.friends: []
方法二:使用 Field
from typing import List from pydantic import BaseModel, Field class User(BaseModel): friends: List[int] = Field(default_factory=list) user1 = User() user1.friends.append(1) print(f"user1.friends: {user1.friends}") # 輸出: user1.friends: [1] user2 = User() print(f"user2.friends: {user2.friends}") # 輸出: user2.friends: []
兩種方法都能確保每次創建 User 實例時,friends 屬性都指向一個新的空列表,避免了實例間數據共享。 推薦使用 Field 方法,因為它更簡潔且直接在模型定義中指定了默認值行為。 記住,對于可變對象,永遠不要直接在類定義中賦值為默認值。
? 版權聲明
文章版權歸作者所有,未經允許請勿轉載。
THE END