如何在 pickle 中加载对象?

How to load object in pickle?

我正在尝试加载一个 pickled 字典,但不断出现这样的属性错误

TypeError: a bytes-like object is required, not '_io.BufferedReader'

下面是读写pickle对象的代码。我正在使用 python 2.7.12 在 linux 工作站上倾倒腌制对象。 3.6.4用python把数据传到Mac,这里执行readTrueData()导致上面的错误。

def readTrueData(name):
    fName = str('trueData/'+name+'.pkl')
    f = open(fName,'rb')
    #    print(f)
    #    print(type(f))
    pC = pickle.loads(f)
    return pC

def storeTrueData(atomicConfigs, name):
    import quippy
    storeDic = {}
    #rangeKeys = len(atomicConfigs)
    #print(rangeKeys)
    qTrain = quippy.AtomsList(atomicConfigs)
    print(len(qTrain))
    rangeKeys = len(qTrain)
    print(rangeKeys)
    for i in range(rangeKeys):
        #configConsidered = atomicConfigs[i]
        trueForce = np.array(qTrain[i].force).T
        storeDic[i] = trueForce
    f = open("trueData/"+ name + ".pkl", "wb")
    pickle.dump(storeDic, f)
    f.close()    
    return None

更新

根据评论中提到的建议,我更改了我的代码如下 a.)pC = pickle.load(f) b.) pC = pickle.loads(f.read()) 在这两种情况下,我都收到以下错误

UnicodeDecodeError: 'ascii' codec can't decode byte 0x87 in position 1: ordinal not in range(128)

如果以这种方式使用 open,您需要使用 pickle.load(...) 才能阅读。

来源:https://docs.python.org/3/library/pickle.html

pC = pickle.loads(f.read()) 是你要找的,但你真的应该使用 with context:

with open(fName, 'rb') as f: 
    pC = pickle.loads(f.read())

这将确保您的文件正确关闭,尤其是因为您的代码在函数中没有 f.close()

您的第一个问题是由参数类型与所选 load* 方法不匹配引起的; loads 需要 bytes 个对象,load 需要文件对象本身。将文件对象传递给 loads 是导致错误的原因。

你的另一个问题是由于 numpydatetime 类型的跨版本兼容性问题; Python 2 个泡菜 str 没有指定编码,但是 Python 3 必须用已知编码(或 'bytes' 解泡它们,以获得原始 bytes 而不是str)。对于 numpydatetime 类型,you're required to pass encoding='latin-1':

Optional keyword arguments are fix_imports, encoding and errors, which are used to control compatibility support for pickle stream generated by Python 2. If fix_imports is true, pickle will try to map the old Python 2 names to the new names used in Python 3. The encoding and errors tell pickle how to decode 8-bit string instances pickled by Python 2; these default to ‘ASCII’ and ‘strict’, respectively. The encoding can be ‘bytes’ to read these 8-bit string instances as bytes objects. Using encoding='latin1' is required for unpickling NumPy arrays and instances of datetime, date and time pickled by Python 2.

无论如何,解决方法是更改​​:

def readTrueData(name):
    fName = str('trueData/'+name+'.pkl')
    f = open(fName,'rb')
    #    print(f)
    #    print(type(f))
    pC = pickle.loads(f)
    return pC

至:

def readTrueData(name):
    fName = str('trueData/'+name+'.pkl')
    with open(fName, 'rb') as f:  # with statement avoids file leak
        # Match load with file object, and provide encoding for Py2 str
        return pickle.load(f, encoding='latin-1')

出于正确性和性能原因,我还建议在 Python 2 机器上将 pickle.dump(storeDic, f) 更改为 pickle.dump(storeDic, f, protocol=2),以便使用更现代的 pickle 协议生成流,一个可以有效地 pickle numpy 数组等的东西。协议 0,默认的 Python 2,不能使用每个字节的最高位(它是 ASCII 兼容的),这意味着原始二进制数据在协议 0 中急剧膨胀,需要大量的位旋转,而协议 2 可以把它生扔掉。协议 2 也是唯一一个有效地 pickle 新样式 类 的 Py2 协议,也是唯一一个可以正确 pickle 某些类型实例的协议(使用 __slots__/__new__ 等的东西)全部.

我还推荐脚本开头:

try:
    import cPickle as pickle
except ImportError:
    import pickle

与 Python 2 一样,pickle 是在纯 Python 中实现的,既慢又无法使用一些更高效的 p​​ickle 代码。在Python3上,cPickle没了,但是pickle自动加速了。在那和使用协议 2 之间,Python 2 机器上的 pickling 应该 运行 much 更快,并且产生 much 更小泡菜。