pickle读入时提示AttributeError Can't get attribute 'XXX' on

Posted by AspenStars on October 1, 2020

解决方案来自StackoverFlow

问题描述

当使用pickle读入数据时data = pickle.load(f),提示AttributeError: Can't get attribute 'XXXX' on <module '__main__' from ' XXX错误

问题分析

名称空间不同造成的错误

当直接运行python文件时,将会以__main__为module名运行程序(无论其实际文件名如何)。 但是,当使用import导入时,则其module名取决于文件名(例如import program时,其中引入的classfunction将以program为module名)

pickle很懒,不会序列化类定义或函数定义,只保存有关如何查找类的reference(类所在的模块及其名称)

所以有可能是dump的时候以__main__为名称空间,load的时候又以具体文件名称为名称空间,所以找不到

因此在使用pickle存储数据的时候应该避免把要pickle的类和函数声明在main模块中

解决方案

三种解决方案

  1. 最简单便捷的方案,不要pickle在__main__模块中的类 可以单独写一个文件来存这个class,然后用import导入

  2. 救急的方案,针对已经存在的,无法重新pickle的文件,写一个自定义反序列化 ```python import pickle

class MyCustomUnpickler(pickle.Unpickler): def find_class(self, module, name): if module == “main”: module = “<填写那个class对应的module名,即导入的文件名>” return super().find_class(module, name)

with open(‘out.pkl’, ‘rb’) as f: unpickler = MyCustomUnpickler(f) obj = unpickler.load()

print(obj) print(obj.name)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
这是加载已经创建的`pickle`文件的最简单的方法。这个程序把责任推给反序列化代码,而实际上应该由序列化代码来正确地创建`pickle`文件

3. **不推荐**,自定义一个序列化方案

与前面的解决方案相比,这个方案可以确保序列化的pickle对象可以被任何人轻松地反序列化,而不需要知道定制的反序列化逻辑。

要做到这一点,可以使用copyreg模块来通知pickle如何反序列化各种类。

所以在这里要做的是告诉pickle反序列化所有main类的实例,需要为每个类注册一个自定义序列化器
```python
import program
import pickle
import copyreg

class MyClass:
    def __init__(self, name):
        self.name = name

def pickle_MyClass(obj):
    assert type(obj) is MyClass
    return program.MyClass, (obj.name,)

copyreg.pickle(MyClass, pickle_MyClass)

if __name__ == '__main__':
    o = MyClass('test')
    with open('out.pkl', 'wb') as f:
        pickle.dump(o, f)