Python尾递归优化

匿名 (未验证) 提交于 2019-12-02 22:51:30

Python开启尾递归优化

cpython本身不支持尾递归优化, 但是一个牛人想出的解决办法:实现一个 tail_call_optimized 装饰器

#!/usr/bin/env python2.4 # This program shows off a python decorator( # which implements tail call optimization. It # does this by throwing an exception if it is # it's own grandparent, and catching such # exceptions to recall the stack.  import sys  class TailRecurseException:     def __init__(self, args, kwargs):         self.args = args         self.kwargs = kwargs  def tail_call_optimized(g):     """     This function decorates a function with tail call     optimization. It does this by throwing an exception     if it is it's own grandparent, and catching such     exceptions to fake the tail call optimization.      This function fails if the decorated     function recurses in a non-tail context.     """     def func(*args, **kwargs):         f = sys._getframe()         if f.f_back and f.f_back.f_back \             and f.f_back.f_back.f_code == f.f_code:             # 抛出异常             raise TailRecurseException(args, kwargs)         else:             while 1:                 try:                     return g(*args, **kwargs)                 except TailRecurseException, e:                     args = e.args                     kwargs = e.kwargs     func.__doc__ = g.__doc__     return func  @tail_call_optimized def factorial(n, acc=1):     "calculate a factorial"     if n == 0:         return acc     return factorial(n-1, n*acc)  print factorial(10000) 

这里解释一下sys._getframe()函数:

sys._getframe([depth]):
Return a frame object from the call stack.
If optional integer depth is given, return the frame object that many calls below the top of the stack.
If that is deeper than the call stack, ValueEfror is raised. The default for depth is zero,
returning the frame at the top of the call stack.

即返回depth深度调用的栈帧对象.

import sys  def get_cur_info():     print sys._getframe().f_code.co_filename  # 当前文件名     print sys._getframe().f_code.co_name  # 当前函数名     print sys._getframe().f_lineno # 当前行号     print sys._getframe().f_back # 调用者的帧

更多关于sys._getframe的使用请看Frame Hacks
说一下tail_call_optimized实现尾递归优化的原理f.f_back.f_back.f_code == f.f_code:, 就捕获当前尾调用函数的参数, 并抛出异常, 从而销毁递归栈并使用捕获的参数手动调用递归函数. 所以递归的过程中始终只存在一个栈帧对象, 达到优化的目的.
为了更清晰的展示开启尾递归优化前、后调用栈的变化和tail_call_optimized装饰器抛异常退出递归调用栈的作用, 我这里利用pudb调试工具做了动图:



通过pudb右边栏的stack, 可以很清晰的看到调用栈的变化.
因为实现了尾递归优化, 所以factorial(10000)都不害怕递归深度限制报错啦!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!