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实现尾递归优化的原理: 当递归函数被该装饰器修饰后, 递归调用在装饰器while循环内部进行, 每当产生新的递归调用栈帧时: f.f_back.f_back.f_code == f.f_code:, 就捕获当前尾调用函数的参数, 并抛出异常, 从而销毁递归栈并使用捕获的参数手动调用递归函数. 所以递归的过程中始终只存在一个栈帧对象, 达到优化的目的.
为了更清晰的展示开启尾递归优化前、后调用栈的变化和tail_call_optimized装饰器抛异常退出递归调用栈的作用, 我这里利用pudb调试工具做了动图:

开启尾递归优化前的调用栈 

开启尾递归优化后(tail_call_optimized装饰器)的调用栈 

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

Python尾递归优化的更多相关文章

  1. python性能优化

      注意:本文除非特殊指明,”python“都是代表CPython,即C语言实现的标准python,且本文所讨论的是版本为2.7的CPython. python为什么性能差: 当我们提到一门编程语言的 ...

  2. Python内存优化

    实际项目中,pythoner更加关注的是Python的性能问题,之前也写过一篇文章<Python性能优化>介绍Python性能优化的一些方法.而本文,关注的是Python的内存优化,一般说 ...

  3. Python内存优化:Profile,slots,compact dict

    实际项目中,pythoner更加关注的是Python的性能问题,之前也写过一篇文章<Python性能优化>介绍Python性能优化的一些方法.而本文,关注的是Python的内存优化,一般说 ...

  4. Scala进阶之路-尾递归优化

    Scala进阶之路-尾递归优化 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 递归调用有时候能被转换成循环,这样能节约栈空间.在函数式编程中,这是很重要的,我们通常会使用递归方法来 ...

  5. .NET 4.6的RyuJIT尾递归优化的Bug

    今天看到园子里有一篇新闻稿.NET 4.6的RyuJIT编译器中发现严重的Bug提到,在.Net 4.6的x64程序中默认启用新的JIT程序RyuJIT在处理尾递归指令的时候有一个Bug,导致无法得到 ...

  6. Python性能优化(转)

    分成两部分:代码优化和工具优化 原文:http://my.oschina.net/xianggao/blog/102600 阅读 Zen of Python,在Python解析器中输入 import ...

  7. python的优化机制与垃圾回收与gc模块

    python属于动态语言,我们可以随意的创建和销毁变量,如果频繁的创建和销毁则会浪费cpu,那么python内部是如何优化的呢? python和其他很多高级语言一样,都自带垃圾回收机制,不用我们去维护 ...

  8. 对SNL语言的解释器实现尾递归优化

    对于SNL语言解释器的内容可以参考我的前一篇文章<使用antlr4及java实现snl语言的解释器>.此文只讲一下"尾递归优化"是如何实现的--"尾递归优化& ...

  9. kotlin递归&尾递归优化

    递归: 对于递归最经典的应用当然就是阶乘的计算啦,所以下面用kotlin来用递归实现阶乘的计算: 编译运行: 那如果想看100的阶乘是多少呢? 应该是结果数超出了Int的表述范围,那改成Long型再试 ...

随机推荐

  1. sql 数字转换为字符串补0

    select right('00000000000'+convert(varchar(5),123),5) select right('00000000000'+cast(123   as   var ...

  2. certification on windows and

    https://jingyan.baidu.com/article/335530dae0eb2319ca41c378.html

  3. [RK3399] ES8316+NS4150 播放视频只有背景音,播放歌曲有的有声音,有的无声音

    CPU:RK3399 系统:Android 音频IC:ES8316 功放IC:NS4150 以前也在 RK3399 上调试过 ES8316,功能都正常,新主板只是更换了功放IC,就出现无声音的问题(仔 ...

  4. C图形化第一步

    之前的贪吃蛇都是在cmd下实现,每次都要调用cls刷新屏幕,简直是闪瞎了我的狗眼. 度娘得知有一种方法可以避免闪烁,即:双缓冲.原理是先在内存中作图,然后将做好的图复制到前台,同时禁止背景刷新. 主要 ...

  5. [spring-boot] 配置随机端口

    第一种: server.port=0 第二种: //自定义一个范围 server.port=${random.int[1024,9999]}

  6. cv2.fillConvexPoly()与cv2.fillPoly()填充多边形

    cv2.fillConvexPoly() cv2.fillConvexPoly()函数可以用来填充凸多边形,只需要提供凸多边形的顶点即可. 我们来画一个三角形 img = np.zeros((1080 ...

  7. GIS地理工具案例教程——合并选中图层

    GIS地理工具案例教程--合并选中图层 商务合作,科技咨询,版权转让:向日葵,135-4855__4328,xiexiaokui#qq.com 功能:并图层列表中 描述:对图层列表中选中图层进行合并. ...

  8. IM 简介

    LayIM - 打造属于你自己的网页聊天系统http://layim.layui.com/ 瓜子IM智能客服系统的数据架构设计(整理自现场演讲) - 知乎https://zhuanlan.zhihu. ...

  9. [转]EXCEL截取字符串中某几位的函数——LeftMIDRight及Find函数的使用

    原文地址:http://blog.sina.com.cn/s/blog_3f136a180102ymq5.html EXCEL截取字符串中某几位的函数 ——Left MID Right及Find函数的 ...

  10. Vue事件 定义方法执行方法 获取数据 改变数据 执行方法传值 以及事件对象

    <template> <div id="app"> <!-- <img v-bind:src='url' /> <img :src= ...