Python 在dataclasses 里,field() 能给可变、不可变数据分别设置安全的默认值

Python 在dataclasses 里,field() 能给可变、不可变数据分别设置安全的默认值

flyfish

Python 里有可变(Mutable) 和 不可变(Immutable)对象

Immutable(不可变对象)

一旦创建出来,里面的内容就不能改、不能动 ,硬改就直接报错。

Python 里自带的不可变类型:

整数int、浮点数float、布尔值bool、字符串str、元组tuple

这类对象创建后,自身状态永远固定,想改只能新建一个。

例子:

字符串"Welcome",想直接改第1个字符,会报错;

元组(0,1,2,3),想把第1个值改成4,也会报错。

Mutable(可变对象)

创建之后,想改就改、想加就加、想删就删,完全灵活。

Python 里自带的可变类型:

列表list、字典dict、集合set,自己写的类一般也都是可变的。

对象本身可以被原地修改,不用新建副本。

例子:

列表[1,2,3],可以append加值、remove删值、直接改某一位;

字典{"name":"Ram"},可以直接修改age对应的值;

集合{1,2,3},可以add添加新元素。

区别

  1. 不可变对象:访问速度更快,但修改成本高(一改就必须造新的);
  2. 可变对象:修改极其方便,适合需要动态增删改的场景。

特殊例外

元组本身是不可变 的,但如果元组里装了列表 这种可变对象:

元组的元素不能替换、删除;

但元组里那个列表的内容,是可以修改的。
容器不可变,但容器里装的可变东西能改

Python 所有数据都存在内存 里,id() 能查看内存地址:

  1. 不可变对象(Immutable)
    创建后,内存里的原值不能改 !强行修改会创建一个新对象,内存地址变了。
  2. 可变对象(Mutable)
    创建后,内存里的原值可以直接修改,对象还是原来的那个,内存地址不变。

id() 打印内存地址,验证:

1. 不可变对象(字符串 str)

python 复制代码
# 不可变对象:字符串
a = "hello"
print("原始值:", a, " 内存地址:", id(a))

# 尝试修改 → 本质是创建新对象
a = a + " world"
print("修改后:", a, " 内存地址:", id(a))

结果

复制代码
# 原始值: hello  内存地址: 1624662037328
# 修改后: hello world  内存地址: 1624662245040

地址变了 = 旧对象没改,新建了一个对象。

2. 可变对象(列表 list)

python 复制代码
# 可变对象:列表
b = [1, 2, 3]
print("原始值:", b, " 内存地址:", id(b))

# 直接修改列表 → 原地修改
b.append(4)
print("修改后:", b, " 内存地址:", id(b))

结果

复制代码
原始值: [1, 2, 3]  内存地址: 2302978101568
修改后: [1, 2, 3, 4]  内存地址: 2302978101568

地址没变 = 直接修改了原对象,没有新建!

类型 原理 内存地址 代表类型
不可变 不能改原值,修改=新建对象 会变化 int/str/float/tuple
可变 可以直接改原值,原地修改 不变 list/dict/set

dataclasses 使用 field() 操作这两类数据

在dataclasses 里,field() 能给**不可变(Immutable)可变(Mutable)**两类对象分别设置安全的默认值

  1. 不可变对象int/str/float/bool/tuple):
    field() 通过 default 参数为其设置静态固定默认值,依托不可变对象无法被修改的特性,不存在数据共享污染风险,完全适配其底层特征。

  2. 可变对象list/dict/set):
    field() 通过 default_factory 参数动态生成独立实例作为默认值,规避了可变对象直接赋值默认值引发的跨实例数据共享问题,适配可变对象可原地修改的特性,保证数据隔离性。

  3. 通用行为控制:
    field()init/repr/compare 等参数,可统一作用于可变与不可变两类对象,精细化控制其在数据类中的初始化、打印展示、等值比较等行为。

不可变类型(数字、字符串):直接用default=固定值

可变类型(列表、字典、集合):不能直接写=[],必须用default_factory

一、default:设置不可变类型默认值

为属性设置静态默认值 (仅支持 str/int/float/bool 等不可变类型)

属性有固定默认值,不需要动态生成,给 int/str/float/bool 这些不能修改的类型,设置一个固定的默认值,就叫不可变类型默认值。

不可变类型 = 一旦创建,里面的值就不能被修改的类型

不可变类型默认值 = 给这类属性设置固定的默认值,用 field(default=xx)

python 复制代码
from dataclasses import dataclass,field
@dataclass
class Book:
    name: str
    # default:设置默认值(不可变类型专用)
    price: float = field(default=29.9)
    author: str = field(default="未知")

# 测试
if __name__ == '__main__':
    book1 = Book("Python入门")
    book2 = Book("Java教程", price=59.9)
    print(book1)
    print(book2)

运行结果

复制代码
Book(name='Python入门', price=29.9, author='未知')
Book(name='Java教程', price=59.9, author='未知')
cpp 复制代码
from dataclasses import dataclass, field

@dataclass
class Student:
    name: str
    # 用 field(default=xx) 设置默认值
    age: int = field(default=18)
    gender: str = field(default="未知")

# 测试
if __name__ == '__main__':
    s1 = Student("小明",gender="男")
    s2 = Student("小红", gender="女")
    print(s1)
    print(s2)

运行结果

复制代码
Student(name='小明', age=18, gender='男')
Student(name='小红', age=18, gender='女')

二、default_factory专门给 list / dict / set 这类可变类型设置默认值

default_factory 就是专门给列表、字典、集合生成独立默认值的工具,每次创建对象都会自动生成一个全新的空数据,让每个对象的默认数据互不干扰,不会出现数据错乱。

列表、字典、集合,想设默认值,只能用 default_factory

cpp 复制代码
from dataclasses import dataclass, field

@dataclass
class StudentGood:
    name: str
    # 安全默认值:每次生成新列表
    scores: list = field(default_factory=list)
    # 扩展:字典、集合
    info: dict = field(default_factory=dict)
    tags: set = field(default_factory=set)

# 测试
if __name__ == '__main__':
    s1 = StudentGood("小明")
    s2 = StudentGood("小红")
    s1.scores.append(90)
    s1.info["school"] = "一中"
    print("小明:", s1.scores, s1.info)
    print("小红:", s2.scores, s2.info)  # 数据隔离,无污染

运行结果

复制代码
小明: [90] {'school': '一中'}
小红: [] {}
cpp 复制代码
from dataclasses import dataclass, field

# 1. 列表默认值(最常用:分数、列表数据)
@dataclass
class Student:
    name: str
    # 空列表默认值
    scores: list = field(default_factory=list)

# 2. 字典默认值(用户信息、配置)
@dataclass
class User:
    name: str
    # 空字典默认值
    info: dict = field(default_factory=dict)

# 3. 集合默认值(标签、去重数据)
@dataclass
class Product:
    name: str
    # 空集合默认值
    tags: set = field(default_factory=set)

# 4. 自定义默认值(默认带初始数据的列表)
def default_scores():
    return [0, 0, 0]  # 初始3个0分

@dataclass
class StudentCustom:
    name: str
    scores: list = field(default_factory=default_scores)

# 5. 组合使用(同时有列表+字典+集合)
@dataclass
class Combo:
    name: str
    my_list: list = field(default_factory=list)
    my_dict: dict = field(default_factory=dict)
    my_set: set = field(default_factory=set)


# 测试所有例子
if __name__ == '__main__':
    # 1 测试列表
    s = Student("小明")
    s.scores.append(99)
    print("学生分数:", s)

    # 2 测试字典
    u = User("小红")
    u.info["age"] = 18
    print("用户信息:", u)

    # 3 测试集合
    p = Product("电脑")
    p.tags.add("电竞")
    print("商品标签:", p)

    # 4 测试自定义默认值
    s2 = StudentCustom("小刚")
    print("初始分数:", s2)

    # 5 测试组合
    c = Combo("测试")
    print("组合数据:", c)

运行结果

复制代码
学生分数: Student(name='小明', scores=[99])
用户信息: User(name='小红', info={'age': 18})
商品标签: Product(name='电脑', tags={'电竞'})
初始分数: StudentCustom(name='小刚', scores=[0, 0, 0])
组合数据: Combo(name='测试', my_list=[], my_dict={}, my_set=set())
相关推荐
西西弗Sisyphus2 小时前
Python @dataclass 有 `__post_init__` 和 无 `__post_init__` 的对比
python·dataclass·__post_init__
独隅2 小时前
PyCharm 开启硬换行的方法
ide·python·pycharm
weixin_408099673 小时前
python请求文字识别ocr api
开发语言·人工智能·后端·python·ocr·api·ocr文字识别
我会好好吃饭歌3 小时前
医疗单据隐私脱敏开源项目:OCR + Vision LLM + 四点定位打码,适配弯曲、旋转、复杂拍摄场景
图像处理·python·开源项目·paddleocr·医疗ai·隐私脱敏
惊鸿若梦一书生3 小时前
《Python 高阶教程》003|变量背后不是盒子:名字、对象与引用的本质
java·jvm·python
qq_380619163 小时前
SQL中如何实现特定范围内数据的批量删除_范围分区与分区删除
jvm·数据库·python
Hommy883 小时前
【开源剪映小助手】云渲染环境搭建
python·开源·github·剪映小助手
qq_380619163 小时前
HTML函数开发需要独立显卡吗_HTML函数与显卡关系详解【说明】
jvm·数据库·python
无语......3 小时前
安装uv并管理 Python / 包
开发语言·python·uv