您正在寻找的只是一个包含类定义的所有重要细节的哈希过程。(可以通过递归包含基类的定义来包含基类。)为了最大限度地减少错误匹配,基本思想是将宽(加密)散列应用于您的类的序列化。所以从以下开始pickle
:它支持的类型比它多,hash
并且当它使用身份时,它使用基于名称的可重现身份。这使它成为递归策略基本案例的一个很好的候选者:处理内容很重要的函数和类,并让它处理引用的任何辅助对象。
因此,按案例定义序列化。如果一个对象属于以下任何一种情况,但最后一种情况,则称它为特殊对象。
- 对于一个
tuple
被认为包含特殊对象的:
- 性格
t
- 其序列化
len
- 每个元素的序列化,按顺序排列
- 对于一个
dict
被认为包含特殊对象的:
- 性格
d
- 其序列化
len
- 每个名称和值的序列化,按排序顺序
- 对于定义突出的类:
- 性格
C
- 其序列化
__bases__
- 其序列化
vars
- 对于定义显着的函数:
- 性格
f
- 其序列化
__defaults__
- 它的序列化
__kwdefaults__
(在 Python 3 中)
- 它的序列化
__closure__
(但使用单元格值而不是单元格本身)
- 其序列化
vars
- 其序列化
__code__
- 对于代码对象(因为
pickle
根本不支持它们):
- 性格
c
co_argcount
它的, co_nlocals
, co_flags
, co_code
, co_consts
, co_names
, co_freevars
, 和,的序列化co_cellvars
顺序;这些都不是特别的
- 对于静态或类方法对象:
- 性格
s
或m
- 其序列化
__func__
- 对于属性:
- 性格
p
fget
它的,fset
和,的序列化fdel
顺序
- 对于任何其他对象:
pickle.dumps(x,-1)
(您实际上永远不会存储所有这些:只需hashlib
在顶级函数中创建您选择的对象,并在递归部分update
中依次使用每个序列化部分。)
类型标签是为了避免冲突,特别是没有前缀。二进制泡菜已经没有前缀了。您可以基于对其内容的确定性分析(即使是启发式的)或上下文做出关于容器的决定,只要您保持一致即可。
与往常一样,平衡误报与误报是一门艺术:对于一个函数,您可以包括__globals__
(修剪已经序列化的对象以避免大的序列化,如果不是无限的序列化)或__name__
其中找到的任何东西。省略co_varnames
忽略重命名局部变量,这很好,除非自省很重要;同样对于co_filename
和co_name
。
您可能需要支持更多类型:查找不pickle
正确的静态属性和默认参数(因为它们包含对特殊类型的引用)或根本不正确。当然请注意,某些类型(如文件对象)是不可提取的,因为很难或不可能序列化它们(尽管与pickle
您在完成code
对象后可以像处理任何其他函数一样处理 lambdas 不同)。冒着错误匹配的风险,您可以选择仅序列化此类对象的类型(与往常一样,以一个字符作为前缀,?
以区别于该位置的实际类型)。