引言
??这篇文章缘何写起呢?是我在使用SMPL人体模型的时候,SMPL官方只提供了Python2版本的模型代码没有提供Python3版本的代码,因此自己升级成了Python3版本,这时候发现Python3没有cPickle模块,因此才会有了这篇文章。查找_pickle在哪那部分看起来可能弱智一些,由于对Python也不是很熟悉所以并不知道_pickle是built-in模块。
??pickle模块是用来序列化和反序列化Python对象的,使用二进制协议。具体怎么使用pickle模块在此不展开,需要的话可以单独再写一篇文章。这篇文章主要讲一下cPickle、pickle和_pickle模块的区别。
cPickle
??cPickle模块是Python2.x中使用的模块。在Pyton2.x版本中普遍使用的模式是:有些模板有一个纯Python版本的模块对应的有一个可选的用C语言实现的加速版本模块。cPickle模块就是用C语言实现的pickle的加速版本模块。
??但是这样对于用户来说比较麻烦,需要用户自己决定使用加速版本还是纯Python版本的模块。如果所有模块都有加速版本这种选择相对来说容易一些,可以都是用加速版本。但实际情况是,不是所有模块都有加速版本,这样用户在使用的时候就需要尝试看看有没有加速版本,没有的话再使用纯Python版本。因此在Python3.x中就取消了这种模块安排方式,无论是加速版本还是纯Python版本,都用同一个模块名。在Python3.x中用户都import标准版本即可,在模块内部进行了处理,如果有加速版本则使用加速版本,没有的话才使用普通版本。
pickle
??pickle模块是Python3.x中使用的模块,在Python3.x中取消了cPickle模块。那在Python3.x中还有对应的用C语言编写的加速版的pickle模块吗?答案是肯定的,只不过被重新命名为了_pickle。
参考:合并C语言版本和Python版本实现的相同接口(interfase)
_pickle
??Python3.x中pickle模块对应的C语言编写的加速版本。
pickle和_pickle关系
前面说过了在Python3.x中使用模块的话直接import标准版本即可。因此在Python3.x中使用pickle模块化话直接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # Use the faster _pickle if possible try: from _pickle import ( PickleError, PicklingError, UnpicklingError, Pickler, Unpickler, dump, dumps, load, loads ) except ImportError: Pickler, Unpickler = _Pickler, _Unpickler dump, dumps, load, loads = _dump, _dumps, _load, _loads |
在这里可以看到会尝试使用加速版的_pickle模块。
_pickle模块在哪
本来到这里就已经把问题搞得很清楚了,但是一根筋的人到这里卡住了,突然想知道那_pickle模块在哪里呢?我怎么能确定有没有这个模块,使用的时候是不是用了加速版的_pickle模块呢?那好在上网搜索了一番之后发现没什么收获,突然想到__file__可以查看模块路径,说干就干:
什么?!(黑人问号脸),难道我记错了,语法不是这样的?再试一下os模块:
__file__确实可以查看模块路径没有问题,那怎么确定_pickle模块在哪里呢?脑子里闪现
原来如此,这个时候才意识到_pickle是built-in模块,惭愧,本人也是“一瓶子不满半瓶子晃荡”。那既然都到这儿了,就搞一下built-in模块在哪存在,是怎么加载的吧。于是把python源码下载下来,查看了一下《Python源码剖析》,没什么头绪,直接在Python源码中找到_pickle的冤源码文件/Modules/_pickle.c然后搜索
1 2 3 4 5 6 7 8 9 10 11 12 13 | struct _inittab _PyImport_Inittab[] = { // ... // 省略...部分内容 {"cmath", PyInit_cmath}, {"errno", PyInit_errno}, {"faulthandler", PyInit_faulthandler}, {"gc", PyInit_gc}, {"math", PyInit_math}, // ... // 省略...部分内容 {"_pickle", PyInit__pickle}, // ... // 省略...部分内容 /* Sentinel */ {0, 0} }; |
原来built-in模块都放在这个结构体中啊,第一部分是模块名称字符串,第二部分是模块的初始化函数。那么只要找到哪里使用了_PyImport_Inittab结构体就明白了,搜索发现PyImport_Inittab指向_PyImport_Inittab结构体,如法炮制,最终找到原来在import.c文件中init_builtin函数会初始化所有的built-in模块。模块名称存在由PyImport_Inittab指向的_PyImport_Inittab结构体,在_PyImport_Inittab中列出了built-in模块的名称以及对应的初始化函数。
built-in模块看来是都整合到解释器里面了,解释器初始化完成这些模块就包含进来了,所以找不到对应的模块文件,至此应该算是真相大白了,也不枉一根筋付出几个小时的查找时间。
如果觉得那里写的不好或者有疑问的可以评论告诉我,我会不断改进,觉得有帮助的点个赞,谢谢
参考
- What difference between pickle and _pickle in python 3?
- 合并C语言版本和Python版本实现的相同接口(interfase)
- What’s New In Python 3.0-Library changes