如何让使用ToFloat描述符的数据类字段变为可选字段?
让带自定义描述符的字段成为可选字段的方案
你现在用自定义ToFloat描述符实现了字段自动转float的功能,但遇到了实例化时必须传所有参数的问题。下面给你两种实用的解决思路,不管用不用@dataclass都能搞定:
方法一:保留@dataclass,给字段设置默认值
@dataclass的规则很简单:没有默认值的字段就是必填项。所以你只需要给每个字段指定默认值,同时确保描述符能正常工作。这里有两种写法:
写法1:将描述符作为类属性,配合dataclass的私有字段
这种方式把描述符和dataclass的字段职责分开,逻辑更清晰:
from dataclasses import dataclass class ToFloat: def __set_name__(self, owner, name): self.public_name = name self.private_name = '_' + name def __get__(self, obj, objtype=None): return getattr(obj, self.private_name) def __set__(self, obj, value): setattr(obj, self.private_name, float(value)) @dataclass class Coordinates: # 把描述符绑定为类属性,负责处理类型转换 lon = ToFloat() lat = ToFloat() val = ToFloat() # 定义dataclass的私有字段,设置默认值 _lon: float = 0.0 _lat: float = 0.0 _val: float = 0.0 # 测试 c = Coordinates() # 无参数实例化成功,c.lon返回0.0 c = Coordinates(_lon=1, _lat="2", _val=3.5) print(c.lon, c.lat, c.val) # 输出 1.0 2.0 3.5
写法2:直接给dataclass字段加默认值,再绑定描述符
这种写法更贴近你原来的代码结构,改动更小:
from dataclasses import dataclass class ToFloat: def __set_name__(self, owner, name): self.public_name = name self.private_name = '_' + name def __get__(self, obj, objtype=None): return getattr(obj, self.private_name) def __set__(self, obj, value): setattr(obj, self.private_name, float(value)) @dataclass class Coordinates: lon: float = 0.0 lat: float = 0.0 val: float = 0.0 # 把描述符绑定到类的对应字段上 Coordinates.lon = ToFloat() Coordinates.lat = ToFloat() Coordinates.val = ToFloat() # 测试 c = Coordinates() print(c.lon) # 0.0 c.lon = "5" print(c.lon) # 5.0
方法二:放弃@dataclass,手动写__init__方法
如果觉得dataclass的规则有点绕,直接自定义类并给__init__参数加默认值是最直接的方式:
class ToFloat: def __set_name__(self, owner, name): self.public_name = name self.private_name = '_' + name def __get__(self, obj, objtype=None): return getattr(obj, self.private_name) def __set__(self, obj, value): setattr(obj, self.private_name, float(value)) class Coordinates: lon = ToFloat() lat = ToFloat() val = ToFloat() def __init__(self, lon=0.0, lat=0.0, val=0.0): self.lon = lon self.lat = lat self.val = val # 测试 c = Coordinates() # 无参数实例化成功 c = Coordinates(lon=1, lat="2", val=3) print(c.lon, c.lat, c.val) # 1.0 2.0 3.0
额外扩展:允许字段为None
如果需要字段可以是None,只需要修改描述符的__set__方法即可:
class ToFloat: def __set_name__(self, owner, name): self.public_name = name self.private_name = '_' + name def __get__(self, obj, objtype=None): return getattr(obj, self.private_name) def __set__(self, obj, value): if value is None: setattr(obj, self.private_name, None) else: setattr(obj, self.private_name, float(value)) class Coordinates: lon = ToFloat() lat = ToFloat() val = ToFloat() def __init__(self, lon=None, lat=None, val=None): self.lon = lon self.lat = lat self.val = val # 测试 c = Coordinates() # 所有字段为None c.lon = "10" print(c.lon) # 10.0
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

