栈帧:实现递归
假设不拼接递归调用 toStr 的结果和 convertString 的查找结果,而是在进行递归调用之前把字符串压入栈中。代码清单4-4 展示了修改后的实现。
代码清单4-4 把字符串压入栈中
from pythonds.basic import Stack
rStack = Stack()
def toStr(n,base):
convertString = "0123456789ABCDEF"
if n < base:
rStack.push(convertString[n])
else:
rStack.push(convertString[n % base])
toStr(n // base, base)
每一次调用 toStr,都将一个字符压入栈中。回到之前的例子,可以发现在第四次调用 toStr 之后,栈中内容如图4-5 所示。因此,只需执行出栈操作和拼接操作,就能得到最终结果 "1010"。

Figure 1. 图4-5 栈中内容
这个例子展示了 Python 如何实现递归函数调用。当调用函数时,Python 分配一个栈帧来处理该函数的局部变量。当函数返回时,返回值就在栈的顶端,以供调用者访问。图4-6 展示了返回语句之后的调用栈。

Figure 2. 图4-6 调用栈示例
注意,调用 toStr(2//2, 2) 将返回值 "1" 放在栈的顶端。之后,这个返回值被用来替换对应的函数调用(toStr(1, 2))并生成表达式 "1" + convertString[2%2]。这一表达式会将字符串 "10" 留在栈顶。通过这种方法,Python 的调用栈取代了代码清单4-4 显式使用的栈。在计算一列数之和的例子中,可以认为栈中的返回值取代了累加变量。
栈帧限定了函数所用变量的作用域。尽管反复调用相同的函数,但是每一次调用都会为函数的局部变量创建新的作用域。
如果记住栈的这种思想,就会发现递归函数写起来很容易。