一个Python递归调用深度的问题

在复杂的自动化测试脚本中,使用eventlet进行超时处理可能导致递归深度超出限制的问题。当模块调用复杂且资源创建时间较长时,会出现`RuntimeError: maximum recursion depth exceeded`。解决方法包括重写超时逻辑,避免使用eventlet的Timeout,或者回退eventlet版本。文中提供了简单的超时重写示例,并建议根据具体环境和需求选择合适的方法。

目录

1.背景

2.问题重现

3.问题解决

1)重写超时

2)对eventlet回退版本

4.参考资料


1.背景

在略微复杂一些的自动化测试脚本中,都会使用“超时”处理。因为采用eventlet进行超时处理,所以有了这一次的经验教训。代码是网上抄的,问题是自己找的。。。

2.问题重现

使用了网上一段超时代码,出处已经无从知晓了,在此向作者抱个歉了~~

import time
import eventlet

TIMEOUT=100

eventlet.monkey_patch()
try:
    with eventlet.Timeout(TIMEOUT):
        while True:
            act = foo(param1, param2)
            if act == exp:
                return True
            time.sleep(5)
except eventlet.timeout.Timeout:
    print('程序运行超时!')

在超时时间较短,模块调用不复杂的情况下,代码不会有出现问题。只是在自己用的自动化框架中,模块较多,调用关系复杂,加之创建资源等待时间较长(超过3min。这个是产品自身属性)。所以出现了,单个用例运行正常,多个用例一起执行时就会抛出如下异常:

RuntimeError: maximum recursion depth exceeded

超过递归调用深度。虽然可以手动临时修改,但毕竟不能从根本上解决问题。

3.问题解决

1)重写超时

简单但没有再出错的代码:

TIMEOUT = 100

def timeout_func(TIMEOUT, act, exp):
    cnt = 1
    while cnt <= TIMEOUT:
        if act == exp:
            break
        time.sleep(5)
        cnt = cnt + 1
    if cnt > TIMEOUT:
        raise Exception(f"超时了~~")

2)对eventlet回退版本

这个如参考资料所示。但由于Python版本非资料中说的3.6,所以这个方法没有考虑。留给各位看官实践吧~~

4.参考资料

Bug #1788253 “SSLContext infinite recursion in Python 3.6 " : Bugs : OpenStack Shared File Systems Service (Manila)

https://github.com/eventlet/eventlet/issues/371 

<think>好的,我现在需要回答用户关于Python递归调用原理及示例的问题。首先,我需要回顾一下递归的基本概念。递归是函数自己调用自己,通常用来解决可以分解为相同子问题的情况。不过,递归需要注意终止条件,否则会导致无限递归,比如栈溢出错误,这在引用[2]中提到过。 接下来,我应该用简单的中文解释递归的原理。可能从阶乘的例子开始,因为这是最经典的递归示例。需要说明递归函数的结构:基本情况(base case)和递归情况。比如阶乘的n=0或1时返回1,否则返回n乘以factorial(n-1)。同时要提到递归的工作流程,比如函数调用栈的形成,以及每次递归如何缩小问题规模。 然后,用户可能需要了解递归的应用场景。根据引用[3],递归常用于树形结构遍历、分治算法如快速排序等。可以举一个目录遍历的例子,或者斐波那契数列,不过斐波那契用递归效率低,这里可以提到尾递归优化或者迭代方法,但Python默认不支持尾递归优化,如引用[2]中的栈深度问题。 还要注意递归的优缺点。优点可能是代码简洁,容易理解;缺点是栈溢出风险和重复计算的问题,比如斐波那契数列中的重复计算。这时候可以提到记忆化(memoization)作为优化手段。 用户提供的引用中,引用[4]给出了一个递归函数的例子,但可能存在错误,比如终止条件是否正确。需要检查那个例子是否正确,比如g(n)的定义是否合理,是否会导致无限递归。例如,如果n从1开始,而终止条件是n==10,那么当调用g(1)时,会递归到g(0)、g(-1)等,直到超过最大递归深度,导致错误。这说明递归的终止条件必须正确设置,否则会引发问题,如引用[2]中的错误。 在示例代码方面,可能需要给出正确的递归例子,比如快速排序的实现,如用户提供的示例中的代码。不过用户的问题是关于递归的原理和示例,所以应该用更基础的例子,比如阶乘、斐波那契数列,或者遍历目录结构。 另外,需要提醒用户递归的注意事项,比如设置正确的终止条件,控制递归深度,或者考虑改用迭代方式。引用[3]中提到递归需要谨慎使用,避免无限递归,这也需要强调。 总结回答的结构:先讲原理,再给出示例代码,然后分析优缺点,最后应用场景和注意事项。同时,生成相关问题,如递归与迭代的区别、如何避免栈溢出、递归在算法中的应用等。</think>### Python递归调用原理及示例 #### 一、递归调用原理 递归是函数**直接或间接调用自身**的过程。其核心在于将复杂问题分解为**规模更小的同类子问题**,直到子问题足够简单可直接求解(称为**基本情况**)。递归通过**函数调用栈**实现,每次递归调用会将当前状态压入栈,直到达到基本情况后逐层返回结果[^3]。 #### 二、示例代码 1. **阶乘计算** $$n! = n \times (n-1)! \quad \text{(递归定义)}$$ ```python def factorial(n): if n == 0 or n == 1: # 基本情况 return 1 else: return n * factorial(n-1) # 递归调用 print(factorial(5)) # 输出120 ``` 2. **遍历嵌套列表** ```python def flatten(lst): result = [] for item in lst: if isinstance(item, list): # 若元素是列表,递归展开 result.extend(flatten(item)) else: result.append(item) return result print(flatten([1, [2, [3, 4], 5]])) # 输出[1, 2, 3, 4, 5] ``` #### 三、优缺点分析 | **优点** | **缺点** | |------------------------------|------------------------------| | 代码简洁易读(如分治算法) | 栈溢出风险(Python默认递归深度约1000层)[^2] | | 天然适合树/图结构处理 | 重复计算(如斐波那契数列递归) | #### 四、注意事项 1. **必须设置终止条件**,否则导致无限递归(如引用[4]中的`g(n)`函数若调用`g(1)`会触发`RecursionError`)。 2. **尾递归优化**:Python未原生支持,可通过装饰器或改写为循环实现[^3]。 3. **性能优化**:对存在重复子问题递归(如斐波那契),使用**记忆化缓存**(`lru_cache`装饰器)。 #### 五、应用场景 - **数据结构遍历**:二叉树、多级目录结构 - **分治算法**:快速排序、归并排序 - **动态规划问题**:背包问题递归+记忆化) - **数学问题**:汉诺塔、组合数计算 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值