V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
SakuraSa
V2EX  ›  问与答

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

  •  1
     
  •   SakuraSa · 2014-07-01 20:19:50 +08:00 · 4218 次点击
    这是一个创建于 3834 天前的主题,其中的信息可能已经有所发展或是发生改变。
    在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   ·   实用小工具   ·   2871 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 13:10 · PVG 21:10 · LAX 05:10 · JFK 08:10
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.