6.1 特征基本原理
你可以在各种对象上附加特征,只要该对象从属于HDF5的树状结构:组、数据集以及命名类型。让我们创建一个包含一个数据集的文件来演示:
dset对象有一个名叫.attrs的属性:
这个小小的代理对象(h5py.AttributeManager的一个实例)允许你通过Python的方式和特征进行互动。你只需要记住这里的attrs对象像组一样以Python字典的方式工作。
比如,你只需要给一个名字赋值就可以创建新的特征:
和Dataset一样,当我们获取元素时,我们会得到一个实际的值,而不是一个中间对象:
和组(以及Python字典)类似,遍历.attrs对象会提供特征的名字:
你会注意到特征的名字和对象名字一样也是以文本字符串方式返回,这在Python 2中就是Unicode,这就解释了上面例子中的u前缀。
删除特征不像删除组那样有严格的规则。你可以随意复用某个名字来改写某个特征:
试图访问不存在的特征会抛出KeyError。和组类似,你无法得知那个不存在的特征的名字:
还有iterkeys、iteritems和values也都按照你希望的方式工作:
基本上一个对象上不会有太多特征,所以对于遍历的性能不需要太过在意。
get方法则跟Group版本的不同,使用的是普通的字典风格的get:
6.1.1 类型猜测
当你创建数据集时,你通常会提供一个NumPy的dtype对象来指定数据类型。如果省略了dtype则会默认使用单精度浮点。每个数据集都有一个显式的dtype,你可以通过.dtype属性查看它:
与数据集相反,h5py会尽量隐藏特征的类型。你要记住,在存入HDF5文件后,特征是有一个确定的类型的。以字典风格的接口特征意味着通常会根据你提供的输入推测出特征的类型。
让我们把文件写进磁盘:
然后用h5ls查看:
大多数情况下,输入的特征值在被传送给np.array并存储到对象中时就自然决定了其类型。对于整型,在32位系统上你会得到一个32位(“本地”)整型:
这解释了上面run_id的“native int”类型。
除了标量,你也可以在文件中存储整个NumPy数组:
当然也有限制。在HDF5默认使用的简洁存储的情况下,特征被限制在64k的大小以内。如果我们试图存储一个(100, 100)的数组,它就会出错:
更糟的是我们发现在这种出错情况下,之前的特征也被清除了:
警告
这是h5py和文件互动时少有的非原子操作之一。在处理大数组特征时要特别小心。
避开这个限制的方法之一是将数据保存在数据集里,然后让一个对象引用(第8章)指向该数据集:
需要访问数据时则通过对象引用来获得数据集并读取:
6.1.2 字符串和文件匹配
特征对于某些类型有特殊处理。首先HDF5对于不同的字符串类型有一个微妙的区别。在上面的例子里,我们给某一个特征赋的值是个Python字符串。这会创建一个变长ASCII字符串(见81页,变长字符串)。
相反,如果你给一个特征赋的值是一个np.string_的实例,在文件中保存的则是一个定长字符串:
一般来说这不成问题,不过某些老版本的FORTRAN程序无法处理变长字符串。如果你的程序遇到此类问题,你需要使用np.string_,或者另一个等效方案——S类型的NumPy数组。
另外,你还可以在文件中保存Unicode字符串。HDF5使用的是UTF-8编码:
使用了定长字符串和Unicode字符串后,现在的文件看上去如下:
关于字符串还有另一点需要提醒,它跟Python 3严格区分字节字符串和文本字符串有关。
当你从文件中读取一个特征时,通常你会得到一个和HDF5内的类型相同的对象。所以如果我们存储的是一个NumPy的int32类型,我们取回的就是一个int32。
而对于Python 3,这意味着大多数HDF5字符串都会被读成字节字符串,处理起来非常别扭。所以在Python 3上,标量字符串总是会在读取时被h5py转换成文本字符串(str类型)。
6.1.3 Python对象
HDF5存储通用Python对象的问题已经是老生常谈了。你会发现在HDF5中你无法将一个Python基本对象保存为特征(或数据集):
这是故意设计成这样的。由错误信息可知,HDF5没有一个“本地”内建类型可以代表Python对象,将对象序列化成一个如HDF5这样的可移植格式被普遍认为是一个坏的设计。将数据存储为“blob”形式会破坏HDF5完美的类型系统和互操作性。
但我不能替你决定你的应用程序该怎么写。如果你确实想要保存Python对象,最好的办法是用pickle将它序列化成一个字符串:
你需要手动追踪哪些字符串是被序列化的对象。这样创建出来的字符串在技术上只支持ASCII字符,所以最好只使用“0”作为pickle的protocol参数。
6.1.4 显式指定类型
有时为了外部兼容性,你创建的特征可能需要非常精确的数据类型,而这是默认的类型猜测无法做到的。或者你的文件可能是从同事那里收到的,所以不想在改写特征值的同时改变特征的类型。
有几种方法做到这一点。.attrs代理对象有一个create方法,它接受一个名字、值以及dtype:
用h5ls查看文件:
这个方法可以确保你获得正确的字符串类型。和字符串标量不一样,如果你提供的是一个字符串数组对象,通过NumPy存入文件后,它们会被默认转换成定长字符串的数组:
作为对比,如果你指定了“变长字符串”作为特殊dtype(第7章):
看文件,两个特征的存储技术有细微的差别。原先的特征被存储为一对14字节的定长字符串,另一个则存储为一对变长字符串:
这看上去只是很小的差异,但是在跟第三方代码合作时,可能就是正常工作的程序和错误消息的天壤之别。
最后,还有另一个便捷方法叫做modify,可以在改变特征值的同时保留特征的类型:
注意如果你提供的值超出了原特征类型的范围,就可能产生意外结果。在这个例子里,意外结果是值会被截断:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论