前言
在使用Pydantic进行数据验证和设置管理时,你可能会遇到一个常见的问题:如何正确设置包含列表、字典或自定义对象的默认值?这时候default_factory就派上用场了。本文将从零开始,用通俗易懂的方式为初学者介绍这个重要概念。
什么是default_factory?
default_factory是Pydantic中Field的一个参数,用于动态创建默认值。简单来说,它接受一个函数(或类),每次需要默认值时就调用这个函数来创建一个全新的对象。
为什么需要default_factory?
问题场景:可变对象的陷阱
让我们先看一个常见的错误:
frompydanticimportBaseModel,FieldfromtypingimportList# 错误的写法classUserConfig(BaseModel):name:str="默认用户"hobbies:List[str]=Field(default=[])# 危险!# 创建两个用户user1=UserConfig()user2=UserConfig()# 给第一个用户添加爱好user1.hobbies.append("游戏")print(f"用户1的爱好:{user1.hobbies}")# ['游戏']print(f"用户2的爱好:{user2.hobbies}")# ['游戏'] ← 问题出现了!问题:两个不同的用户实例竟然共享了同一个爱好列表!这是因为default=[]创建的是同一个列表对象,所有实例都指向它。
解决方案:使用default_factory
frompydanticimportBaseModel,FieldfromtypingimportList# 正确的写法classUserConfig(BaseModel):name:str="默认用户"hobbies:List[str]=Field(default_factory=list)# 每次调用list()创建新列表# 创建两个用户user1=UserConfig()user2=UserConfig()# 给第一个用户添加爱好user1.hobbies.append("游戏")print(f"用户1的爱好:{user1.hobbies}")# ['游戏']print(f"用户2的爱好:{user2.hobbies}")# [] ← 正确!default vs default_factory 对比
| 特性 | default | default_factory |
|---|---|---|
| 用途 | 静态默认值 | 动态默认值 |
| 适用场景 | 不可变对象(字符串、数字等) | 可变对象(列表、字典、自定义类等) |
| 创建时机 | 类定义时创建一次 | 每次实例化时创建 |
| 是否共享 | 是(可变对象有风险) | 否(每次都是新对象) |
代码示例对比
fromdatetimeimportdatetimefrompydanticimportBaseModel,FieldclassConfigExample(BaseModel):# 适合用 default 的场景app_name:str=Field(default="我的应用")version:str=Field(default="1.0.0")debug:bool=Field(default=False)# 适合用 default_factory 的场景tags:list=Field(default_factory=list)settings:dict=Field(default_factory=dict)created_at:datetime=Field(default_factory=datetime.now)嵌套模型中的default_factory
在复杂应用中,我们经常需要嵌套模型:
frompydanticimportBaseModel,FieldclassDatabaseConfig(BaseModel):host:str="localhost"port:int=5432database:str="myapp"classAppConfig(BaseModel):app_name:str="我的应用"# 错误写法# database: DatabaseConfig = Field(default=DatabaseConfig) # 传递类本身# 也是错误的# database: DatabaseConfig = Field(default=DatabaseConfig()) # 所有实例共享# 正确写法database:DatabaseConfig=Field(default_factory=DatabaseConfig)# 测试app1=AppConfig()app2=AppConfig()print(app1.databaseisapp2.database)# False - 不同的实例高级用法:自定义工厂函数
default_factory可以接受任何可调用对象:
frompydanticimportBaseModel,Fieldfromdatetimeimportdatetime,timedeltadefcreate_default_settings():"""自定义工厂函数"""return{"theme":"dark","language":"zh-CN","timeout":30}deffuture_time():"""返回一小时后的时间"""returndatetime.now()+timedelta(hours=1)classUserProfile(BaseModel):username:str# 使用自定义函数settings:dict=Field(default_factory=create_default_settings)# 使用lambda表达式permissions:list=Field(default_factory=lambda:["read","write"])# 使用其他函数expires_at:datetime=Field(default_factory=future_time)# 测试user=UserProfile(username="张三")print(user.settings)# {'theme': 'dark', 'language': 'zh-CN', 'timeout': 30}print(user.permissions)# ['read', 'write']print(user.expires_at)# 一小时后的时间实现原理简析
default_factory的工作原理其实很简单:
# 简化的内部逻辑classField:def__init__(self,default_factory=None):self.default_factory=default_factorydefget_default_value(self):ifself.default_factory:returnself.default_factory()# 调用函数创建新对象returnNone# 当你创建模型实例时# model = MyModel()# 内部会调用 field.get_default_value() 来获取默认值常见错误和解决方案
错误1:混淆default和default_factory
# 错误tags:list=Field(default=list)# 传递了函数对象,不是调用结果# 正确tags:list=Field(default_factory=list)# 传递函数,让Pydantic调用错误2:在default_factory中传递实例
# 错误config:DatabaseConfig=Field(default_factory=DatabaseConfig())# 传递了实例# 正确config:DatabaseConfig=Field(default_factory=DatabaseConfig)# 传递类错误3:忘记处理可变默认值
# 危险的写法defcreate_user(hobbies=[]):# 默认参数陷阱return{"hobbies":hobbies}# 正确的写法defcreate_user(hobbies=None):ifhobbiesisNone:hobbies=[]return{"hobbies":hobbies}实际应用场景
场景1:配置管理
frompydantic_settingsimportBaseSettingsfrompydanticimportFieldclassDatabaseConfig(BaseSettings):host:str="localhost"port:int=5432max_connections:int=20classAppSettings(BaseSettings):app_name:str="MyApp"database:DatabaseConfig=Field(default_factory=DatabaseConfig)classConfig:env_prefix="APP_"场景2:API响应模型
frompydanticimportBaseModel,FieldfromtypingimportList,OptionalfromdatetimeimportdatetimeclassApiResponse(BaseModel):success:bool=Truemessage:str="操作成功"data:Optional[dict]=Field(default_factory=dict)errors:List[str]=Field(default_factory=list)timestamp:datetime=Field(default_factory=datetime.now)总结
default_factory是Pydantic中处理默认值的强大工具,特别适用于:
- 可变对象:列表、字典、集合等
- 嵌套模型:包含其他Pydantic模型的字段
- 动态值:需要在运行时计算的默认值
- 复杂对象:需要调用构造函数创建的对象
记住核心原则:
- 不可变对象用
default - 可变对象用
default_factory - 需要动态创建的对象用
default_factory
掌握了default_factory,你就能避免很多常见的陷阱,写出更安全、更可维护的代码!
相关阅读:
- Pydantic官方文档
- Python可变与不可变对象详解
希望这篇文章对你有帮助!如果你有任何问题,欢迎在评论区讨论。