Python:递归深度超过最大限制。

94
我有下面的递归代码,在每个节点上调用SQL查询以获取属于父节点的节点。
这是错误信息:
Exception RuntimeError: 'maximum recursion depth exceeded' in <bound method DictCursor.__del__ of <MySQLdb.cursors.DictCursor object at 0x879768c>> ignored

RuntimeError: maximum recursion depth exceeded while calling a Python object
Exception AttributeError: "'DictCursor' object has no attribute 'connection'" in <bound method DictCursor.__del__ of <MySQLdb.cursors.DictCursor object at 0x879776c>> ignored

我调用的获取 SQL 结果的方法:

def returnCategoryQuery(query, variables={}):
    cursor = db.cursor(cursors.DictCursor);
    catResults = [];
    try:
        cursor.execute(query, variables);
        for categoryRow in cursor.fetchall():
            catResults.append(categoryRow['cl_to']);
        return catResults;
    except Exception, e:
        traceback.print_exc();

实际上,我对上述方法没有任何问题,但我还是放在这里以便更好地概述问题。

递归代码:

def leaves(first, path=[]):
    if first:
        for elem in first:
            if elem.lower() != 'someString'.lower():
                if elem not in path:
                    queryVariable = {'title': elem}
                    for sublist in leaves(returnCategoryQuery(categoryQuery, variables=queryVariable)):
                        path.append(sublist)
                        yield sublist
                    yield elem

调用递归函数

for key, value in idTitleDictionary.iteritems():
    for startCategory in value[0]:
        print startCategory + " ==== Start Category";
        categoryResults = [];
        try:
            categoryRow = "";
            baseCategoryTree[startCategory] = [];
            #print categoryQuery % {'title': startCategory};
            cursor.execute(categoryQuery, {'title': startCategory});
            done = False;
            while not done:
                categoryRow = cursor.fetchone();
                if not categoryRow:
                    done = True;
                    continue;
                rowValue = categoryRow['cl_to'];
                categoryResults.append(rowValue);
        except Exception, e:
            traceback.print_exc();
        try:
            print "Printing depth " + str(depth);
            baseCategoryTree[startCategory].append(leaves(categoryResults))
        except Exception, e:
            traceback.print_exc();

打印字典的代码,

print "---Printing-------"
for key, value in baseCategoryTree.iteritems():
    print key,
    for elem in value[0]:
        print elem + ',';
    raw_input("Press Enter to continue...")
    print

如果递归太深,我应该在调用递归函数时得到错误,但实际上当我打印字典时出现了这个错误。


8
改用迭代方式重写,而非递归方式。 - Seth Carnegie
1
if first: 检查与 for elem in first: 重复。如果查询返回一个空的结果列表,那么对它进行迭代将会仅仅正确地什么都不做,正如你所期望的那样。此外,你可以使用列表推导式更简单地创建该列表(分号是不必要的,通常被认为很丑陋 :))。 - Karl Knechtel
@KarlKnechtel 对不起,关于分号的问题,你能看出我刚开始学习Python编程吗.... :) - add-semi-colons
不用道歉,毕竟我不是雇你来写的 :) 希望你会发现 Python 很解放 ;) - Karl Knechtel
1个回答

178

您可以增加允许的堆栈深度 - 这样,更深层次的递归调用将会成为可能,例如:

import sys
sys.setrecursionlimit(10000) # 10000 is an example, try with different values

...但我建议你首先尝试优化你的代码,例如使用迭代而不是递归。


19
它被设置为1000有原因...我相信Guido van Rossum在这方面说过一些话 - Lambda Fairy
4
Guido的论点是,正确的尾递归调用会提供更差的堆栈跟踪——相比于在迭代式编写时根本没有堆栈帧,这是什么意思呢?那更糟糕在哪里?如果我们给他们一些好东西,他们可能会开始依赖它。我不相信这个,它闻起来像Scheme。Python设计不好,所以编译器无法有效地发现某些内容是否为尾调用。我想这一点我们可以达成共识吧? - John Clements
1
@hajef 你的评论很友善!所以当我不同意其中任何一个时,请不要误解。首先,你提到了具有中缀运算符和无括号的函数式语言:看看 Haskell 和 ML,这两种流行的函数式语言都具有中缀运算符和无括号。其次,你认为“清晰度包括堆栈跟踪”;如果你想要堆栈跟踪,在一个正确的尾调用语言中,你可以通过保留固定数量的帧或保留所有调用跟踪来启用它们——无论你想要什么。关键是在一个正确的尾调用语言中,你不是被迫这样做。 - John Clements
3
@hajef 第三,尾调用绝不仅适用于列表;任何树形结构都可以。试着在循环中遍历一棵树而不使用递归调用,你最终会手动模拟堆栈。最后,你的论点Python从未以这种方式设计是确实正确的,但这并不能说服我它是一个好的设计。 - John Clements
3
仅仅因为van Rossum对递归有着特别的偏好,并不意味着递归而非迭代就一定是“最优”的:这取决于你要优化什么! - Gene Callahan
显示剩余11条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接