在 Python 中,递归指函数在执行过程中再次调用自身。它适用于可分解为规模更小、结构相同的子问题,例如阶乘定义或树形目录遍历。编写递归时须同时明确基线条件(何时停止)与递归步骤(如何把问题缩小)。若缺少基线或基线不可达,调用将持续叠加直至触发 RecursionError。本节以阶乘为例说明展开过程,并讨论与循环的取舍及深度限制。

阶乘的递归实现

n 的阶乘可定义为 n 乘以 (n−1) 的阶乘,并规定 1 的阶乘为 1。后者为基线,前者为递归步骤:

Python 递归求阶乘示例运行
def factorial(n):
    if n == 1:
        return 1
    return n * factorial(n - 1)

print(factorial(5))    # 120

基线条件与递归步骤

  • 基线条件:规模足够小时直接返回结果,不再调用自身。缺少基线时调用将一直叠加,最终引发 RecursionError。
  • 递归步骤:将当前问题化为规模更小的同类问题,调用自身并在返回后组合出当前答案。

factorial(4) 的展开

调用先递推至基线,再沿返回链回溯相乘。左侧为递推拆小,右侧为回溯汇总:

递推:把问题拆小 回溯:结果一路带回 factorial(4) = 4 × factorial(3) factorial(3) = 3 × factorial(2) factorial(2) = 2 × factorial(1) factorial(1) → 基线:返回 1 触发回溯 返回 1 返回 2 返回 6 返回 24
factorial(4) 先一路递推到基线 factorial(1),拿到 1 后再从下往上回溯:1 → 2 → 6 → 24。调用栈先压后弹。
factorial(4)
= 4 * factorial(3)
= 4 * (3 * factorial(2))
= 4 * (3 * (2 * factorial(1)))
= 4 * (3 * (2 * 1))
= 24

斐波那契数列

每一项为前两项之和,自然出现两次递归调用。下列朴素实现在 n 较大时会重复计算大量子问题,思路清楚但性能较差;工程上常改用循环或缓存:

def fib(n):
    if n <= 1:
        return n
    return fib(n - 1) + fib(n - 2)

for i in range(8):
    print(fib(i), end=" ")
print()

递归与循环

维度递归循环
表达贴近分治、树形定义须自行维护状态变量
开销每次调用占一层栈帧,过深可能溢出通常开销更小
适用目录树、表达式树、分治多数线性重复任务

递归深度上限

CPython 默认将递归深度限制在大约一千层,超出则抛出 RecursionError。可用 sys.setrecursionlimit 调大,但深度过大往往表明应改进算法,而非仅提高限额:

import sys
sys.setrecursionlimit(5000)

目录树遍历

子目录内仍可包含子目录,结构本身适合递归。用 os 列出当前层,遇目录再调用自身,可遍历整棵树:

import os

def walk(dir_path, depth=0):
    for item in os.listdir(dir_path):
        full = os.path.join(dir_path, item)
        print("  " * depth + item)
        if os.path.isdir(full):
            walk(full, depth + 1)

walk(".")

注意事项

  • 缺少基线条件,或基线永远达不到,将导致无法终止的递归。
  • 基线条件错误(例如阶乘使用 n == 0 却从负数调用)可能导致错误结果或无法终止。
  • 深度过大时优先改为循环、显式栈或带缓存的分治,而非仅提高递归上限。

本节说明了 Python 中递归的组成与常见用法。下一节介绍类与对象:__init__、self 及实例方法。