更高效的字符串序列化方法

9

在pickle模块对数据进行序列化时,似乎使用了字符串转义字符,这会导致处理numpy数组等大型数据时效率低下。建议采用以下方式改善:

z = numpy.zeros(1000, numpy.uint8)
len(z.dumps())
len(cPickle.dumps(z.dumps()))

长度分别为1133个字符和4249个字符。
z.dumps()的输出类似于"\x00\x00" (字符串中实际上有零),但pickle似乎使用了该字符串的repr()函数,得到"'\x00\x00'"(零为ascii零)。
即 ("0" in z.dumps() == False) and ("0" in cPickle.dumps(z.dumps()) == True)。

你应该在这里的帖子中添加一个具体的问题。 - John Feminella
你想序列化一个Python字符串还是一个numpy字节数组? - jfs
1
应该是 len(cPickle.dumps(z))。 - vartec
4个回答

24

尝试使用pickle协议的较新版本,可以通过在pickle.dumps()中使用protocol参数来指定。默认值为0,表示ASCII文本格式。建议使用大于1的版本(我建议使用pickle.HIGHEST_PROTOCOL)。协议格式1和2(以及3,但仅适用于py3k)是二进制格式,应该更加节省空间。


Python 3 默认使用协议 3。 - Cees Timmerman

9

解决方案:

import zlib, cPickle

def zdumps(obj):
  return zlib.compress(cPickle.dumps(obj,cPickle.HIGHEST_PROTOCOL),9)

def zloads(zstr):
  return cPickle.loads(zlib.decompress(zstr))  

>>> len(zdumps(z))
128

以下是有关此主题的更多信息:http://tinyurl.com/3ymhaj5。基本上,如果您要序列化到磁盘,则可以使用gzip.open()而不是open。 - emil.p.stanchev
@slack3r 那个链接已经失效了。 - kynan
'ascii' 编解码器无法在位置 1 编码字符 u'\xda':序数不在范围内(128) - High schooler

3

z.dumps() 是已经被 pickle 序列化的字符串,可以使用 pickle.loads() 进行反序列化:

>>> z = numpy.zeros(1000, numpy.uint8)
>>> s = z.dumps()
>>> a = pickle.loads(s)
>>> all(a == z)
True

1
一个对vartec答案的改进,看起来更加节省内存(因为它不会强制将所有内容转换为字符串):
def pickle(fname, obj):
    import cPickle, gzip
    cPickle.dump(obj=obj, file=gzip.open(fname, "wb", compresslevel=3), protocol=2)

def unpickle(fname):
    import cPickle, gzip
    return cPickle.load(gzip.open(fname, "rb"))

-1 (1) 不要硬编码协议号,使用“-1”或“HIGHEST_PROTOCOL”。(2) 后续的压缩是一个附加功能,与他的问题无关。(3) 在解压缩时指定“compresslevel”是没有意义的;任何必要的解压缩文件的信息都将存储在压缩文件的头部中--否则,如果您不知道使用了什么压缩级别,如何解压缩文件呢? - John Machin
(1) 那么py2代码将无法读取py3对象。(2) 头部说“对vartec答案的改进”,它使用了压缩--我认为它使用了更少的内存,但这可能是一个错误的印象...(3) 已修复。 - gatoatigrado

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接