前言:
pickle的功能就是把你上次计算得到的数据保存起来,当你需要使用这些数据时,直接通过reload把数据恢复了就行,这样的好处有:
- 被pickle的数据,在被多次reload时,不需要重新去计算得到这些数据,这样节省计算机资源,如果你不pickle,你每调用一次数据,就要计算一次。
- 通过pickle的数据,被reload时,可以更好的被内存调用,不需要经过数据格式的转换。(即:python自带的file函数只能存储和读取字符串格式的数据.pickle可以存储和读取成其他格式比如list dict的数据)
有人可能觉得,我直接通过open把数据写到一个txt文档也能达到以上的效果,但是这样做的结果是,你能够达到pickle的功能,把数据保存起来,但是当你再去调用这些数据时,你的txt格式的数据,没有pickle的数据读取更高效。
另外还有一点,你通过open把数据存储到txt中时的效率,就不如pickle的效率高。
综上,你如果只是做一次的数据存储和调用,以及数据量很小的情况下,你可以用open等方法保存数据和调用数据,但是当你需要通过大量计算得到一个数据,同时后期还会多次使用这个数据时,pickle的节省计算机资源的效果就出来了。举例:比如说构建机器学习模型,例如决策树,一般情况都是先建树,后剪枝,再预测,但这样有一个缺点,即明明是一棵树上跑测试数据,但每次都需要建树,决策树的大部分时间都浪费在建树上了,所以我们可以在第一次跑完时用pickle把整个树保存起来,以后测试时直接load进来预测或剪枝就好了,这样做节省了时间。
例:
defprocess_initialize(context):
g.__query = query(valuation)
恢复过程是这样的:
- 加载策略代码, 因为python是动态语言, 编译即运行, 所以全局的(在函数外写的)代码会被执行一遍.
- 使用保存的状态恢复 g, context, 和函数外定义的全局变量.
- 执行 process_initialize, 每次启动时都会执行这个函数.
- 如果策略代码和上一次运行时发生了修改,而且代码中定义了 after_code_changed 函数,则会运行after_code_changed函数.
- 模拟盘更改回测之后上述的全局变量(包括 g 和 context 中保存的)不会丢失. 新代码中 initialize 不会执行.
如果需要修改原来的值, 可以在 after_code_changed 函数里面修改, 比如, 原来代码是:a = 1 def initialize(context): g.stock = '000001.XSHE'
代码改成:
a = 2 def initialize(context): g.stock = '000002.XSHE'
执行时, a 仍然是 1, g.stock 仍然是 ‘000001.XSHE’, 要修改他们的值, 必须定义 after_code_changed:
def after_code_changed(context): global a a = 2 g.stock = '000002.XSHE'
正文:
在程序运行的过程中,所有的变量都是在内存中,比如,定义一个dict:
d = dict(name='Bob', age=20, score=88)
可以随时修改变量,比如把name
改成'Bill'
,但是一旦程序结束,变量所占用的内存就被操作系统全部回收。如果没有把修改后的'Bill'
存储到磁盘上,下次重新运行程序,变量又被初始化为'Bob'
。
我们把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening等等,都是一个意思。
序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。
反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。
Python提供两个模块来实现序列化:cPickle
和pickle
。这两个模块功能是一样的,区别在于cPickle
是C语言写的,速度快,pickle
是纯Python写的,速度慢,跟cStringIO
和StringIO
一个道理。用的时候,先尝试导入cPickle
,如果失败,再导入pickle
:
try:
import cPickle as pickle
except ImportError:
import pickle
首先,我们尝试把一个对象序列化并写入文件:
>>> d = dict(name='Bob', age=20, score=88)
>>> pickle.dumps(d)
"(dp0\nS'age'\np1\nI20\nsS'score'\np2\nI88\nsS'name'\np3\nS'Bob'\np4\ns."
pickle.dumps()
方法把任意对象序列化成一个str,然后,就可以把这个str写入文件。或者用另一个方法pickle.dump()
直接把对象序列化后写入一个file-like Object:
>>> f = open('dump.txt', 'wb')
>>> pickle.dump(d, f)
>>> f.close()
看看写入的dump.txt
文件,一堆乱七八糟的内容,这些都是Python保存的对象内部信息。
当我们要把对象从磁盘读到内存时,可以先把内容读到一个str
,然后用pickle.loads()
方法反序列化出对象,也可以直接用pickle.load()
方法从一个file-like Object
中直接反序列化出对象。我们打开另一个Python命令行来反序列化刚才保存的对象:
>>> f = open('dump.txt', 'rb')
>>> d = pickle.load(f)
>>> f.close()
>>> d
{'age': 20, 'score': 88, 'name': 'Bob'}
变量的内容又回来了!
当然,这个变量和原来的变量是完全不相干的对象,它们只是内容相同而已。
Pickle的问题和所有其他编程语言特有的序列化问题一样,就是它只能用于Python,并且可能不同版本的Python彼此都不兼容,因此,只能用Pickle保存那些不重要的数据,不能成功地反序列化也没关系。