from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel
class User(BaseModel):
friends: List[int] = []
user_1 = User()
user_1.friends.append(1)
print(user_1.friends)
user_2 = User()
print(user_2.friends)
上面的代码,运行后输出如下:
[1]
[]
我有一个疑问,就是 friends 的默认值是一个 []
空列表,通过前后两次实例化,两个实例对象持有的 friends 为什么指向的不是同一个 list 呢?
如果去掉继承 BaseModel,输出的两个就都是 [1]
了
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel
class User():
friends: List[int] = []
user_1 = User()
user_1.friends.append(1)
print(user_1.friends)
user_2 = User()
print(user_2.friends)
输出
[1]
[1]
pydantic 的 BaseModel 施加了什么“魔法”?
主要是默认值带来的问题。
列表是可变对象,当没有指定默认值时,每个实例都会创建一个新的空列表对象。但是,在指定了默认值后,所有实例都会共享同一个默认值对象,因为默认值只会创建一次并在所有实例之间共享。因此,user_1和user_2的属性friends内存指向相同。
如果继承了BaseModel,每个实例都会创建一个新的默认值对象,因为Pydantic会在内部创建一个新的配置类,以确保每个实例都有自己的默认值。因此如果实例化多个User类,并访问它们的friends属性,每个实例的friends属性都应该具有不同的内存地址。
在
pydantic/main.py
(早期的实现,为了更容易说明这个问题)中,可以看到这个过程:在这个元类中,当创建一个新的类时,会检查这个类是否继承自BaseModel。如果是,则在内部创建一个新的配置类,并将该类的默认值、验证器等信息保存在类的属性中。这样,每个实例都会有自己的默认值对象,从而确保其属性内存指向不同。
如果是最新的版本,可能是出于性能以及声明式范式的考虑,对这个过程实现做了分离。