一种框架,它支持用单元描述物理对象并易于序列化的类。
unitdoc的Python项目详细描述
unitdoc
unitdoc是一个python库,用于处理用单元描述物理对象并易于序列化的数据对象。让我们看一个例子。首先,导入unitdoc并创建将在应用程序中使用的注册表:
fromunitdocimportUnitDocRegistryudr=UnitDocRegistry()
让我们创建一个表示电池的类
importattr@udr.serialize()@attr.s()classBattery(object):name=attr.ib()weight=udr.attrib(default='45g')volume=udr.attrib(default='16ml',default_unit='ml')capacity=udr.attrib(default='3.0Ah')voltage=udr.attrib(default='3.6V',description='Average voltage')
我们可以创建一个Battery
的实例,它将是一个普通的attr
对象(参见attrs library),它具有一个很好的repr
函数:
a_battery=Battery(name='battery',weight='43g')print(a_battery)# outputs: Battery(name='battery', weight=<Quantity(43, 'gram')>, volume=<Quantity(16, 'milliliter')>, capacity=<Quantity(3.0, 'Ah')>, voltage=<Quantity(3.6, 'volt')>)
我们可以在PINT软件包允许的任何操作中使用电池的属性:
energy=(a_battery.capacity*a_battery.voltage).to('Wh')energy_density=(energy/a_battery.weight).to('Wh/kg')print(f'{energy} @ {energy_density}')# outputs: 10.8 Wh @ 251.2 Wh / kg
现在,让我们保存并重新加载电池对象:
# look at serialized formprint(a_battery.serialize())# outputs:name:batteryweight:!unit43gvolume:!unit16mlcapacity:!unit3Ahvoltage:!unit3.6V
这可以很容易地保存到文件中并重新加载:
fn='a_battery.yaml'# save to yaml filewithopen(fn,'w')asf:f.write(a_battery.serialize())# load from yaml filewithopen(fn,'r')asf:a_loaded_battery=Battery.deserialize(f.read())asserta_battery==a_loaded_battery
更多功能
unitdoc促进了某些操作,这些操作可以改进代码。
如果在属性中指定default_unit
,数量将自动规范化为该单位:
a_battery=Battery(name='battery',volume='15903 mm^3')print(a_battery.volume)# outputs: 15.9 ml
如果指定了default_unit
,任何不兼容的单元都将引发异常:
fromunitdocimportDimensionalityErrortry:a_battery=Battery(name='battery',volume='42 g')exceptDimensionalityErrorase:print(e)# outputs: Cannot convert from 'gram' ([mass]) to 'milliliter' ([length] ** 3)
您可以检索参数的说明,例如数据表示代码
fromunitdocimportget_attr_descriptionprint(get_attr_description(a_battery.__class__,'voltage'))# outputs: Average voltage
安装
使用包管理器pip安装unitdoc:
pip install unitdoc
或者,从git安装最新版本:
git clone https://github.com/deniz195/unitdoc python unitdoc/setup.py install --user
相关套餐
unitdoc基于以下令人惊叹的软件包:
- pint处理单位
- ruamel.yamls处理半结构化数据(嵌套字典)的(反)序列化
- attrs处理数据类的样板文件
- cattr处理用于(反)序列化的类的非结构化和重组
unitdocregistry为每个包创建注册表/转换器/解析器并聚合它们。您可以利用每个软件包的功能:
从PINT使用单位注册表:
q=udr.ureg('1000gram').to('kg')print(q)# outputs: 1 kg
使用ruaml中的yaml解析器。yaml:
q_yaml=udr.yaml.dump(dict(weight=q))print(q_yaml)# outputs: weight: !unit 1 kg
使用cattr转换器:
@udr.serialize()@attr.s()classThing(object):weight=udr.attrib(default='45g',description='Total weight')a_thing=Thing()a_thing_dict=udr.cattr.unstructure(a_thing)asserttype(a_thing_dict)==dictprint(a_thing_dict['weight'])# output: 45 g
限制
考虑到attrs包的限制,安全地更新属性需要一定的预防措施。例如,给定上面的Battery
类,以下是可能的,但不是理想的
a_battery=Battery(name='battery')a_battery.volume=99type(a_battery.volume)# outputs: int
这是不可取的,因为没有执行单元检查和规范化。
避免这种情况(和其他问题)的一个好方法是只使用关键字(kw_only=True
)和冻结(frozen=True
)attr
对象。
@udr.serialize()@attr.s(kw_only=True,frozen=True)classBetterBattery(object):name=attr.ib()weight=udr.attrib(default='45g')volume=udr.attrib(default='16ml',default_unit='ml')capacity=udr.attrib(default='3.0Ah')voltage=udr.attrib(default='3.6V',description='Average voltage')
仅限关键字的限制将不允许从位置参数创建对象,因此以下行将失败,并出现类型错误:
a_battery=BetterBattery('battery','42g','16ml')
这很好,因为当数据模型随时间变化时,位置参数可能很危险。下一行创建一个新对象,如果类发生更改,则该对象是稳定的
a_battery=BetterBattery(name='battery',weight='42g',volume='16ml')
冻结的实例限制不允许更改对象,因此此行将失败,并显示FrozenInstanceError
a_battery.volume=99
要更新值,可以使用attr.evolve函数,该函数使用更新的值创建新对象
a_battery=attr.evolve(a_battery,volume='12cm^3')
在这种情况下,按预期执行单位转换和检查。
虽然unitdoc与常规的attr
类(@attr.s()
)一起工作,但我们强烈建议使用@attr.s(kw_only=True, frozen=True)
。
贡献
欢迎拉取请求。对于重大变更,请先打开一个问题来讨论您希望更改的内容。
请确保根据需要更新测试。