• 记忆函数装饰器-避免函数重复计算的利器


    注意,此装饰器的设计前提是:对于相同参数,函数一定返回相同的值.如果某个函数,相同参数可能返回不同值,或者修改了一些外部数据,那么最好不要用此装饰器.

    有时候对于一些函数,我们需要用相同参数多次调用,且不方便将其预先储存在一个变量中.

    例如这种,假设有函数f,g,h,m:

    list=[[f(i),g(f(i)),h(g(f(i))),m(f(i))] for i in range(100)]

    那么,在计算g(f(i)),要先重复计算一次f(i),再计算g.

    h(g(f(i)))同理.如果这些函数的计算过程很复杂,那么就浪费了大量资源.

    再例如很经典的,非尾递归形式的斐波拉契函数:

    def fibonacci(n):
        print "call fibonacci"
        if n in (0, 1):
            return n
        return fibonacci(n-1) + fibonacci(n-2)

    你想要计算fibonacci(100),你会发现非常非常慢.因为它最终等于计算一大堆fibonacci(1)和fibonacci(0)之和.简直是重复计算的典范了.

    这还是第一层重复计算.如果非常不幸,你还要重复调用fibonacci(100),那么简直无法想象后果.

    对于这种浪费,可以让函数自己记忆每次被调用的参数和结果信息.

    为了方便应用,我们可以设计一个函数装饰器,凡是经过它装饰的函数,在被调用时都会首先检查传入的参数是不是以前有过的,如果是,那么直接返回结果;如果不是,那么就进行计算,保存此次参数和函数值,再返回结果.

    def memory(function):
        cache = {}
        def memofunc(*nkw,**kw):
            key=str(nkw)+str(kw)
            if key not in cache:            
                cache[key] = function(*nkw,**kw)
            return cache[key]
        return memofunc
        
    
    @memory
    def fibonacci(n):
        print "call fibonacci"
        if n in (0, 1):
            return n
        return fibonacci(n-1) + fibonacci(n-2)
    
    
    @memory
    def func(a,b,*nkw,**kw):
        print 'call func'
        return a+b+sum(nkw)+sum(kw.values())
    
    print '-'*20
    print fibonacci(10)
    print '-'*20
    print fibonacci(10)
    print '-'*20
    print func(1,2,3,4,5,x=6,y=7)
    print '-'*20
    print func(1,2,3,4,5,x=6,y=7)

    最终结果:

    >>> 
    --------------------
    call fibonacci
    call fibonacci
    call fibonacci
    call fibonacci
    call fibonacci
    call fibonacci
    call fibonacci
    call fibonacci
    call fibonacci
    call fibonacci
    call fibonacci
    55
    --------------------
    55
    --------------------
    call func
    28
    --------------------
    28

    测试发现,上面的装饰器可以让fib在1秒内算出fib(332).更大的数就会报错了.

    大家可以比较一下去掉那个装饰器之后,Python要多久才算得出fib(332).

    此外,对于一些只接受1个参数的函数来说,还可以有个简单的装饰器版本,性能会更高一些:

    def memory(function):
        cache = {}
        def memofunc(x):
            if x not in cache:
                cache[x] = function(x)
            return cache[x]
        return memofunc

    用这个装饰器,可以计算到fib(498)

  • 相关阅读:
    Button 的CommandName 用法
    如何循序渐进向DotNet架构师发展
    用sqlserver进行分布式查询(链接服务器)(转)
    关于.resx
    OO设计原则总结
    为ASP.NET 2.0网站生成唯一程序集
    依赖倒置、控制反转和依赖注入辨析(转)
    通过http地址获取页面内容
    Sql同表去除重复
    动态启动WCF服务
  • 原文地址:https://www.cnblogs.com/xiangnan/p/3382284.html
Copyright © 2020-2023  润新知