使用Python的Pickle / cPickle打击最大recursion深度

背景:我正在构build一个代表一个字典,使用最小的构buildalgorithm。 input列表是4.3M utf-8string,按字典顺序sorting。 生成的图是非循环的,最大深度为638个节点。 我的脚本的第一行通过sys.setrecursionlimit()将recursion限制设置为1100。

问题是:我希望能够将我的trie序列化到磁盘,所以我可以将它加载到内存中,而无需从头开始重build(大约22分钟)。 我已经尝试pickle.dump()cPickle.dump() ,与文本和二进制协议。 每一次,我得到一个堆栈跟踪,如下所示:

  File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/pickle.py", line 649, in save_dict self._batch_setitems(obj.iteritems()) File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/pickle.py", line 663, in _batch_setitems save(v) File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/pickle.py", line 286, in save f(self, obj) # Call unbound method with explicit self File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/pickle.py", line 725, in save_inst save(stuff) File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/pickle.py", line 286, in save f(self, obj) # Call unbound method with explicit self File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/pickle.py", line 648, in save_dict self.memoize(obj) RuntimeError: maximum recursion depth exceeded 

我的数据结构相对简单: trie包含对启动状态的引用,并定义了一些方法。 dfa_state包含一个布尔型字段,一个string字段和一个从标签到状态的字典映射。

我并不是很熟悉pickle的内部工作 – 我的最大recursion深度是否需要大于或等于某个n的深度的n倍? 或者这可能是由我不知道的其他东西引起的?

更新:将recursion深度设置为3000没有帮助,所以这条道路看起来并不怎么样。

更新2:你们是对的; 由于缺省recursion限制,我认为泡菜会使用较小的嵌套深度,因此我目光短浅。 一万个伎俩。

从文档 :

试图挑选一个高度recursion的数据结构可能会超过最大recursion深度,在这种情况下会引发一个RuntimeError。 你可以用sys.setrecursionlimit()小心地提高这个限制。

尽pipe您的trie实现可能很简单,但它使用recursion,并且在转换为持久数据结构时可能会导致问题。

我的build议是继续提高recursion限制,以查看您正在使用的数据和您正在使用的实施scheme的数据是否有上限。

除此之外,如果可能的话,可以尝试将树实现更改为“较lessrecursion”,或者编写一个具有内置数据持久性(在实现中使用pickles和shelves ) 的附加实现 。 希望有所帮助

泡菜确实需要recursion地走你的树。 如果腌菜只使用5个级别的函数调用来完成工作,那么深度638将需要设置的级别超过3000。

尝试一个更大的数字,recursion限制真的只是为了保护用户不必等待太久,如果recursion落在一个无限的空间。

腌菜处理周期好,所以即使你的特里在那里有一个周期也没关系

仔细检查你的结构确实是非循环的。

你可以尝试进一步提高极限。 有一个硬件最大的平台依赖,但尝试50000是合理的。

也尝试酸洗一个微不足道的细节版本。 如果腌菜即使只存储了三个字母的单词,那么你知道有一个根本性的问题,而不是腌制。 但是,如果只有当你尝试存储10k字时才会发生这种情况,那么这可能是腌制平台限制的缺陷。

为了防止段错误,还必须使用resource.setrlimit来增加堆栈大小

如果只使用sys.setrecursionlimit ,那么如果达到Linux内核允许的最大堆栈大小,仍然可以进行segfault。

这个值可以通过resource.setrlimit来增加,如下所示: 在python脚本中设置stacksize

 import pickle import resource import sys print resource.getrlimit(resource.RLIMIT_STACK) print sys.getrecursionlimit() max_rec = 0x100000 # May segfault without this line. 0x100 is a guess at the size of each stack frame. resource.setrlimit(resource.RLIMIT_STACK, [0x100 * max_rec, resource.RLIM_INFINITY]) sys.setrecursionlimit(max_rec) a = [] # 0x10 is to account for subfunctions called inside `pickle`. for i in xrange(max_rec / 0x10): a = [a] print pickle.dumps(a, -1) 

另请参见: Python中的最大recursion深度是多less,以及如何增加?

默认的最大值是8Mb。

testing在Ubuntu 16.10,Python 2.7.12。

我的需求是有点直接,所以我通过保存我的字典在.txt格式解决了这个问题。 唯一的一点是,当你再次加载你的文件时,你必须把它转换回字典。

 import json # Saving the dictionary with open('filename.txt', 'w') as file_handle: file_handle.write(str(dictionary)) # Importing the .txt file with open('filename.txt', 'r') as file_handle: f = '"' + file_handle.read() + '"' # From .txt file to dictionary dictionary = eval(json.loads(f)) 

如果这不起作用,您可以尝试使用json格式导出字典。