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

2014-07-01 20:19:50 +08:00
 SakuraSa
在Python中,如何使pythonic地实现 实例的函数只计算一次,之后每次都返回第一次的值?
4205 次点击
所在节点    问与答
14 条回复
raptium
2014-07-01 20:22:02 +08:00
decorator
SakuraSa
2014-07-01 20:31:15 +08:00
这个修饰器该如何写呢?
修饰器返回的函数应该是怎样的参数形式呢?
我想参照@property、@classmethod、@staticmethod的源码,但是鉴于水平,看不懂……
如果能有一个小例子的话,就万分感谢了。
jianghu52
2014-07-01 20:36:56 +08:00
decorator 好久之前好像看到过一个这个方法,思想就是用new函数来判定,如果new函数被执行过一次之后,那么就不再执行。如果没被执行,那么就执行一次
binux
2014-07-01 20:43:07 +08:00
def cached(f):
...._cache = {}
....@functools.wraps(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
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
2014-07-01 21:34:43 +08:00
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
2014-07-01 21:38:49 +08:00
感谢@binux ,用这样的方法成功了
SakuraSa
2014-07-01 21:40:25 +08:00
感谢@riaqn ,functools这么好用一起竟然没有听说过,真是惭愧……
SakuraSa
2014-07-01 21:41:46 +08:00
@binux 另外,您给出的例子中有一点小错(wrapper参数中不应该带self
Niris
2014-07-01 22:42:56 +08:00
用 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
2014-07-02 09:34:56 +08:00
@SakuraSa 因为你要“实例的函数”,正确的做法应该是将 cache 存在 self 里面
SakuraSa
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
2014-07-02 10:26:13 +08:00
@SakuraSa 你这个还是 cache 到类上的,只有当定义类时调用了 @cache ,只创建一个 LruCache,这里的 self 指的是 LruCache。
我原来写的本来就是给普通函数用的,cache 到类上应该是

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

这样的
SakuraSa
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存放的位置应该和实例一致了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/120474

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX