前言在使用Pydantic进行数据验证和设置管理时你可能会遇到一个常见的问题如何正确设置包含列表、字典或自定义对象的默认值这时候default_factory就派上用场了。本文将从零开始用通俗易懂的方式为初学者介绍这个重要概念。什么是default_factorydefault_factory是Pydantic中Field的一个参数用于动态创建默认值。简单来说它接受一个函数或类每次需要默认值时就调用这个函数来创建一个全新的对象。为什么需要default_factory问题场景可变对象的陷阱让我们先看一个常见的错误frompydanticimportBaseModel,FieldfromtypingimportList# 错误的写法classUserConfig(BaseModel):name:str默认用户hobbies:List[str]Field(default[])# 危险# 创建两个用户user1UserConfig()user2UserConfig()# 给第一个用户添加爱好user1.hobbies.append(游戏)print(f用户1的爱好:{user1.hobbies})# [游戏]print(f用户2的爱好:{user2.hobbies})# [游戏] ← 问题出现了问题两个不同的用户实例竟然共享了同一个爱好列表这是因为default[]创建的是同一个列表对象所有实例都指向它。解决方案使用default_factoryfrompydanticimportBaseModel,FieldfromtypingimportList# 正确的写法classUserConfig(BaseModel):name:str默认用户hobbies:List[str]Field(default_factorylist)# 每次调用list()创建新列表# 创建两个用户user1UserConfig()user2UserConfig()# 给第一个用户添加爱好user1.hobbies.append(游戏)print(f用户1的爱好:{user1.hobbies})# [游戏]print(f用户2的爱好:{user2.hobbies})# [] ← 正确default vs default_factory 对比特性defaultdefault_factory用途静态默认值动态默认值适用场景不可变对象字符串、数字等可变对象列表、字典、自定义类等创建时机类定义时创建一次每次实例化时创建是否共享是可变对象有风险否每次都是新对象代码示例对比fromdatetimeimportdatetimefrompydanticimportBaseModel,FieldclassConfigExample(BaseModel):# 适合用 default 的场景app_name:strField(default我的应用)version:strField(default1.0.0)debug:boolField(defaultFalse)# 适合用 default_factory 的场景tags:listField(default_factorylist)settings:dictField(default_factorydict)created_at:datetimeField(default_factorydatetime.now)嵌套模型中的default_factory在复杂应用中我们经常需要嵌套模型frompydanticimportBaseModel,FieldclassDatabaseConfig(BaseModel):host:strlocalhostport:int5432database:strmyappclassAppConfig(BaseModel):app_name:str我的应用# 错误写法# database: DatabaseConfig Field(defaultDatabaseConfig) # 传递类本身# 也是错误的# database: DatabaseConfig Field(defaultDatabaseConfig()) # 所有实例共享# 正确写法database:DatabaseConfigField(default_factoryDatabaseConfig)# 测试app1AppConfig()app2AppConfig()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(hours1)classUserProfile(BaseModel):username:str# 使用自定义函数settings:dictField(default_factorycreate_default_settings)# 使用lambda表达式permissions:listField(default_factorylambda:[read,write])# 使用其他函数expires_at:datetimeField(default_factoryfuture_time)# 测试userUserProfile(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_factoryNone):self.default_factorydefault_factorydefget_default_value(self):ifself.default_factory:returnself.default_factory()# 调用函数创建新对象returnNone# 当你创建模型实例时# model MyModel()# 内部会调用 field.get_default_value() 来获取默认值常见错误和解决方案错误1混淆default和default_factory# 错误tags:listField(defaultlist)# 传递了函数对象不是调用结果# 正确tags:listField(default_factorylist)# 传递函数让Pydantic调用错误2在default_factory中传递实例# 错误config:DatabaseConfigField(default_factoryDatabaseConfig())# 传递了实例# 正确config:DatabaseConfigField(default_factoryDatabaseConfig)# 传递类错误3忘记处理可变默认值# 危险的写法defcreate_user(hobbies[]):# 默认参数陷阱return{hobbies:hobbies}# 正确的写法defcreate_user(hobbiesNone):ifhobbiesisNone:hobbies[]return{hobbies:hobbies}实际应用场景场景1配置管理frompydantic_settingsimportBaseSettingsfrompydanticimportFieldclassDatabaseConfig(BaseSettings):host:strlocalhostport:int5432max_connections:int20classAppSettings(BaseSettings):app_name:strMyAppdatabase:DatabaseConfigField(default_factoryDatabaseConfig)classConfig:env_prefixAPP_场景2API响应模型frompydanticimportBaseModel,FieldfromtypingimportList,OptionalfromdatetimeimportdatetimeclassApiResponse(BaseModel):success:boolTruemessage:str操作成功data:Optional[dict]Field(default_factorydict)errors:List[str]Field(default_factorylist)timestamp:datetimeField(default_factorydatetime.now)总结default_factory是Pydantic中处理默认值的强大工具特别适用于可变对象列表、字典、集合等嵌套模型包含其他Pydantic模型的字段动态值需要在运行时计算的默认值复杂对象需要调用构造函数创建的对象记住核心原则不可变对象用default可变对象用default_factory需要动态创建的对象用default_factory掌握了default_factory你就能避免很多常见的陷阱写出更安全、更可维护的代码相关阅读Pydantic官方文档Python可变与不可变对象详解希望这篇文章对你有帮助如果你有任何问题欢迎在评论区讨论。