ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python面向对象:@dataclass数据类简化类定义

Python面向对象:@dataclass数据类简化类定义 一、开篇告别样板代码写一个数据容器类需要多少代码__init__、__repr__、__eq__……大部分都是重复的样板代码。dataclassPython 3.7就是来拯救你的——它自动生成这些方法。⌨️ 先看对比# ❌ 传统写法——大量样板代码classPointOld:def__init__(self,x,y):self.xx self.yydef__repr__(self):returnfPointOld(x{self.x}, y{self.y})def__eq__(self,other):ifnotisinstance(other,PointOld):returnNotImplementedreturnself.xother.xandself.yother.y# ✅ dataclass写法——三行搞定fromdataclassesimportdataclassdataclassclassPoint:x:floaty:floatp1Point(3.0,4.0)p2Point(3.0,4.0)print(p1)# Point(x3.0, y4.0) —— 自动__repr__print(p1p2)# True —— 自动__eq__print(p1.x,p1.y)# 3.0 4.0二、dataclass的核心特性2.1 自动生成的方法fromdataclassesimportdataclassdataclassclassProduct:name:strprice:floatstock:int0# 默认值category:str通用# 自动生成的方法# __init__ —— 初始化方法# __repr__ —— 可读的字符串表示# __eq__ —— 值的相等比较# ✅ __init__pProduct(机械键盘,399,stock50)print(p.name,p.price)# 机械键盘 399# ✅ __repr__print(p)# Product(name机械键盘, price399, stock50, category通用)# ✅ __eq__p2Product(机械键盘,399,stock50)print(pp2)# True —— 比较的是值不是内存地址2.2 字段默认值和field()函数fromdataclassesimportdataclass,field# ⚠️ 可变默认值的陷阱——在dataclass中同样存在# dataclass# class Bad:# items: list [] # ❌ 所有实例共享同一个列表# ✅ 使用field(default_factory...)解决dataclassclassShoppingCart:items:listfield(default_factorylist)# 每次创建新列表created_at:strfield(default_factorylambda:2024-01-01)user_id:intfield(default0)defadd_item(self,item):self.items.append(item)# 每个实例有自己独立的列表cart1ShoppingCart()cart2ShoppingCart()cart1.add_item(苹果)cart2.add_item(香蕉)print(cart1.items)# [苹果]print(cart2.items)# [香蕉] —— 各自独立# field()的可选参数# default: 默认值# default_factory: 生成默认值的可调用对象# init: 是否作为__init__参数默认True# repr: 是否出现在__repr__中默认True# compare: 是否参与比较默认True# hash: 是否参与哈希默认None自动# metadata: 附加元数据框架/库可用dataclassclassUser:username:strpassword:strfield(reprFalse)# 不在__repr__中显示_id:intfield(default0,initFalse,reprFalse)# 内部IDroles:listfield(default_factorylist,compareFalse)# 不参与比较userUser(zhangsan,secret123)print(user)# User(usernamezhangsan) —— password隐藏了三、高级特性3.1post_init—— 初始化后处理fromdataclassesimportdataclass,fielddataclassclassRectangle:width:floatheight:floatarea:floatfield(initFalse)# 不在__init__中def__post_init__(self):在__init__之后自动调用——做额外的初始化self.areaself.width*self.height# 可以在这里做验证ifself.width0orself.height0:raiseValueError(宽和高必须大于0)rectRectangle(5,3)print(rect.area)# 15# __post_init__的常见用途# 1. 计算派生值area width * height# 2. 数据验证值是否合法# 3. 注册实例如自动加入全局列表3.2 不可变数据类frozenTruefromdataclassesimportdataclassdataclass(frozenTrue)# 冻结——实例不可变classImmutablePoint:x:floaty:floatpImmutablePoint(3.0,4.0)print(p.x,p.y)# 3.0 4.0# p.x 5.0 # dataclasses.FrozenInstanceError!# 冻结的dataclass实例不可修改# frozenTrue的用途# - 作为字典的键自动生成__hash__# - 线程安全的数据共享# - 配置/常量对象# frozen dataclass可以用作set的元素和dict的键points{ImmutablePoint(1,2),ImmutablePoint(3,4),ImmutablePoint(1,2)}print(len(points))# 2 —— 重复的被去掉了3.3 slotsTruePython 3.10fromdataclassesimportdataclassdataclass(slotsTrue)# 使用__slots__节省内存classPoint3D:x:floaty:floatz:floatimportsys p1Point3D(1,2,3)# 没有__dict__——内存更小# print(p1.__dict__) # AttributeError# 不能动态添加属性# p1.color red # AttributeError四、实战案例4.1 配置对象fromdataclassesimportdataclass,fieldfromtypingimportListdataclassclassDatabaseConfig:host:strlocalhostport:int5432database:strmydbuser:stradminpassword:strfield(default,reprFalse)dataclassclassAppConfig:app_name:strMyAppdebug:boolFalsedatabase:DatabaseConfigfield(default_factoryDatabaseConfig)allowed_hosts:List[str]field(default_factorylambda:[localhost])secret_key:strfield(default,reprFalse)# 使用configAppConfig(app_name生产环境,debugFalse,databaseDatabaseConfig(hostdb.example.com,port5433),allowed_hosts[example.com,api.example.com],)print(config)4.2 将dataclass转为dict/JSONfromdataclassesimportdataclass,asdict,astupleimportjsondataclassclassAddress:city:strstreet:strzipcode:strdataclassclassPerson:name:strage:intaddress:Address personPerson(张三,30,Address(北京,长安街,100000))# 转为字典dasdict(person)print(d)# {name: 张三, age: 30, address: {city: 北京, street: 长安街, zipcode: 100000}}# 转为JSONjson_strjson.dumps(asdict(person),ensure_asciiFalse,indent2)print(json_str)# 转为元组print(astuple(person))# (张三, 30, (北京, 长安街, 100000))# 从字典创建fromdataclassesimportfield person2Person(**d)# 注意address需要是Address对象不是dict五、总结dataclass是Python 3.7以来最受欢迎的特性之一。它消除了数据类的样板代码让你专注于数据本身而非重复的__init__和__repr__。核心参数参数作用frozenTrue实例不可变slotsTrue使用__slots__节省内存orderTrue自动生成比较方法, 等field(default...)默认值不可变field(default_factory...)默认值可变每次新创建✅什么时候用dataclass当你定义一个主要存数据的类时——配置、DTO、值对象、请求/响应模型。
返回列表