如何 pythonic 地实现函数只计算一次? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
SakuraSa
V2EX    问与答

如何 pythonic 地实现函数只计算一次?

  •  1
     
  •   SakuraSa 2014-07-01 20:19:50 +08:00 4599 次点击
    这是一个创建于 4128 天前的主题,其中的信息可能已经有所发展或是发生改变。
    在Python中,如何使pythonic地实现 实例的函数只计算一次,之后每次都返回第一次的值?
    第 1 条附言    2014-07-01 21:44:55 +08:00
    感谢 binux 与 riaqn ,帮助解决了这个问题。
    以前都不知道functools的存在,看来是该好好看看python文档了
    第 2 条附言    2014-07-01 23:17:25 +08:00
    感谢 Niris
    Niris给出的链接正是我所面对的问题最Pythonic的解决方案
    https://github.com/defnull/bottle/blob/master/bottle.py#L194-L218

    如果是带参数的情况,riaqn 给出的解决方案最简洁
    可惜functools.lru_cache是py3.3中才添加的
    和我一样用py2.7的话,可以参考https://github.com/cleverdeng/LruCache.py

    至此问题已经完美解决了,十分感谢大家的帮助~
    14 条回复    2014-07-02 10:58:48 +08:00
    raptium
        1
    raptium  
       2014-07-01 20:22:02 +08:00 via Android   1
    decorator
    SakuraSa
        2
    SakuraSa  
    OP
       2014-07-01 20:31:15 +08:00
    这个修饰器该如何写呢?
    修饰器返回的函数应该是怎样的参数形式呢?
    我想参照@property、@classmethod、@staticmethod的源码,但是鉴于水平,看不懂……
    如果能有一个小例子的话,就万分感谢了。
    jianghu52
        3
    jianghu52  
       2014-07-01 20:36:56 +08:00
    decorator 好久之前好像看到过一个这个方法,思想就是用new函数来判定,如果new函数被执行过一次之后,那么就不再执行。如果没被执行,那么就执行一次
    binux
        4
    binux  
       2014-07-01 20:43:07 +08:00   1
    def cached(f):
    ...._cache = {}
    [email protected](f)
    ....def wrapper(self, *args, **kwargs):
    ........key = '-|-'.join(map(unicode, args))\
    ................+'-|-'.join('%s-:-%s' % x for x in kwargs.iteritems())
    ........if key in _cache:
    ............return _cache[key]
    ........ret = f(*args, **kwargs)
    ........_cache[key] = ret
    ........return ret
    ....return wrapper
    messense
        5
    messense  
       2014-07-01 20:45:07 +08:00
    class A(object):
    ----def cal_once(self):
    --------if not hasattr(self, '_cal_once'):
    ------------# do whatever you want
    ------------print('there you go')
    ------------self._cal_Once= 'xxx'
    --------return self._cal_once
    类似这样?
    riaqn
        6
    riaqn  
       2014-07-01 21:34:43 +08:00   1
    Example of efficiently computing Fibonacci numbers using a cache to implement a dynamic programming technique:

    @lru_cache(maxsize=None)
    def fib(n):
    if n < 2:
    return n
    return fib(n-1) + fib(n-2)

    >>> [fib(n) for n in range(16)]
    [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

    >>> fib.cache_info()
    CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)

    New in version 3.2.

    Changed in version 3.3: Added the typed option.


    https://docs.python.org/3.3/library/functools.html#functools.lru_cache
    SakuraSa
        7
    SakuraSa  
    OP
       2014-07-01 21:38:49 +08:00
    感谢@binux ,用这样的方法成功了
    SakuraSa
        8
    SakuraSa  
    OP
       2014-07-01 21:40:25 +08:00
    感谢@riaqn ,functools这么好用一起竟然没有听说过,真是惭愧……
    SakuraSa
        9
    SakuraSa  
    OP
       2014-07-01 21:41:46 +08:00
    @binux 另外,您给出的例子中有一点小错(wrapper参数中不应该带self
    Niris
        10
    Niris  
       2014-07-01 22:42:56 +08:00   1
    用 Descriptor。
    例子可以看 https://github.com/defnull/bottle/blob/master/bottle.py#L194-L218

    不明白 @property、@classmethod、@staticmethod 的话,官方有个 howto:
    https://docs.python.org/3/howto/descriptor.html
    binux
        11
    binux  
       2014-07-02 09:34:56 +08:00   1
    @SakuraSa 因为你要“实例的函数”,正确的做法应该是将 cache 存在 self 里面
    SakuraSa
        12
    SakuraSa  
    OP
       2014-07-02 09:57:43 +08:00
    @binux 的确如你所说,如果直接去掉self参数,实际上相当于把cache保存在类上,而非实例上
    这样会导致相应cache所占的内存不能随着实例自然释放
    但是如果不修改,代码似乎并不能符合预期的工作:
    http://codepad.org/2N6QMAWW

    我现在的解决方案是:
    https://github.com/SakuraSa/LruCache.py/blob/master/lru.py
    使用其中的Cache修饰器
    binux
        13
    binux  
       2014-07-02 10:26:13 +08:00   1
    @SakuraSa 你这个还是 cache 到类上的,只有当定义类时调用了 @cache ,只创建一个 LruCache,这里的 self 指的是 LruCache。
    我原来写的本来就是给普通函数用的,cache 到类上应该是

    def cache(f):
    ..def wrapper(self, *args, **kwargs):
    ....self._cache[key]

    这样的
    SakuraSa
        14
    SakuraSa  
    OP
       2014-07-02 10:58:48 +08:00
    感谢@binux
    根据你的提示,得到了想要的结果http://codepad.org/QMl6motG
    另外LruCache.py也做了相应的修改
    https://github.com/SakuraSa/LruCache.py/blob/master/lru.py#L114-125
    现在cache存放的位置应该和实例一致了
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2667 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 12:20 PVG 20:20 LAX 05:20 JFK 08:20
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86