我正在尝试了解pickle
模块的行为。
在我看来,pickle
不保存方法,只保存属性。
以下是我对代码的意思:
import pickle
class Person:
def __init__(self, name):
self.name = name
def add_dot(self):
self.name += '.'
etienne = Person('etienne')
pickled = pickle.dumps(etienne)
class Person:
def __init__(self, name):
self.name = name
def add_dot(self):
self.name += '..'
etienne = pickle.loads(pickled)
etienne.add_dot()
您希望此代码返回什么?
有人能解释我的这种行为吗?
你试过吗?添加行CCD_ 3并运行它返回"0";艾蒂安&";,但更好的问题是为什么
pickle类实例时需要记住的重要一点是,类实例实际上由两部分组成:实例变量(调用myClass.__dict__
查看它们的样子)和告诉python您希望如何解释这些数据的源代码。当您在类的实例上调用方法(如etienne.add_dot()
)时,它会被转换为对类中方法的基本函数调用,同时将实例作为self
的参数传入(因此etienne.add_dot()
变为Person(etienne)
)。这种方案被广泛用于OOP,因为当你运行带有大量类实例的程序时,它有助于节省空间,因为每个实例都可以存储实例变量,并在调用了方法时引用相同的源代码。
正如您所注意到的,pickle
模块在酸洗类时使用了完全相同的方案,只保存实例变量,而不保存类本身的源代码。来自泡菜文档:
这是故意的,因此您可以修复类中的错误或向类中添加方法,并且仍然加载使用早期版本的类创建的对象。
简单的答案是否定的,你不能pickle方法,但你可以pickle从模块的顶层(使用def,而不是lambda)访问的函数(内置和用户定义的)
Python文档指出,可以进行pickled和unpickled的类型有:
- 无、真和假
- 整数、浮点数、复数
- 字符串、字节、字节数组
- 仅包含可拾取对象的元组、列表、集合和字典
- 函数(内置和用户定义),可从模块的顶层访问(使用def,而不是lambda)
- 可从模块的顶层访问的类
- 此类实例的__dict__或调用__getstate__()的结果是可拾取的(有关详细信息,请参阅拾取类实例一节)
根据维基百科的定义,序列化为:
将数据结构或对象状态转换为可以存储的格式(例如,在文件或内存数据中缓冲器)或传输(例如通过计算机网络),以及稍后重建(可能在不同的计算机环境中)。
Pickle模块不是为了序列化方法而构建的。主要目的是序列化属性的状态。然后在取消拾取时实例化对象。由于该方法是类定义的一部分,因此您的代码可以正常工作,但只能用于稍后的类定义。因此CCD_ 10的值最终为";艾蒂安">。
在实例的上下文中,保存类定义通常也是不充分和不可取的,因为关于如何使用序列化数据的指令将来可能会更改。