Pydantic中,可變對象默認值為何會造成實例間數據共享差異?

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 為了避免此問題,會在每次實例化時創建新的默認值對象,確保每個實例擁有獨立的屬性,避免了意外的副作用。

Pydantic中,可變對象默認值為何會造成實例間數據共享差異?

解決方案:

為了避免這個問題,應該使用工廠函數或 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
喜歡就支持一下吧
點贊9 分享