本篇文章的内容主要包含
- 利用Python弱引用存储字典缓存类的实例,让参数相同的实例不用重复生成
- 略过复杂的通用化代码编写,利用Python自带库来缓存实例和方法对象
在Python的许多库中都有缓存实例的例子,比如logging模块的Logger类实例
import logging
a = logging.getLogger("abc")
b = logging.getLogger("abc")
print(a is b)
弱引用(weakref)通常用于缓存或映射数据量较大的对象,当你使用python字典存放例如key为name,value为一个很大的图像对象,或者反过来,引用和修改这个图像对象时始终是“实时的”,因为它实际的存在字典中。但是利用弱引用中的WeakKeyDictionary 和WeakValueDictionary ,你引用这个图像时只需要使用它的名字即可,它不需要占用很大的空间来存储,实际上弱引用就是一个对象指针。
创建实例缓存的一种方式是写一个方法,每次都通过这个方法访问实例
import weakref
class Person:
def __init__(self, name):
print("person initializing")
self.name = name
_cache_instance = weakref.WeakValueDictionary()
def get_person(name):
if name not in _cache_instance:
instance = Person(name)
_cache_instance[name] = instance
return instance
else:
return _cache_instance[name]
def test():
q = get_person("q")
e = get_person("q")
print(q is e)
r = get_person("q")
print(e is r)
if __name__ == '__main__':
test()
输出
person initializing
True
True
从输出可以看出3次调用参数相同的Person只实例了一次
看到这里很容易联想到使用装饰器来简化代码
import weakref
from functools import wraps
_cache_instance = weakref.WeakValueDictionary()
def instance_cache(cls_instance):
@wraps(cls_instance)
def inner(name, *args, **kwargs):
if name not in _cache_instance:
instance = cls_instance(name, *args, **kwargs)
_cache_instance[name] = instance
else:
instance = _cache_instance[name]
return instance
return inner
@instance_cache
class Person:
def __init__(self, name):
print("person initializing")
self.name = name
def test():
q = Person("abc")
e = Person("abc")
print(q is e)
r = Person("abc")
print(e is r)
if __name__ == '__main__':
test()
输出
person initializing
True
True
输出在期望之内,但这个装饰器要实现到更通用化还是不够,想要把存入的参数通用化作为键并且适用于不同的类实例和方法并不是一件简单的事情,幸好Python为我们提供了一个非常好用和方便的最近最久未使用的缓存方法functools.lru_cache
from functools import lru_cache
@lru_cache()
class Person:
def __init__(self, fist_name, last_name=None):
print("person initializing")
self.first_name = fist_name
self.last_name = last_name
def set_last_name(self, name):
self.last_name = name
def test():
p1 = Person("abc", "d")
p2 = Person("abc", "d")
print(p1 is p2)
p3 = Person("abc", "d")
print(p2 is p3)
def test1():
import json
d1 = {"a": [1, 2], "b": "3"}
d2 = {"a": [1, 2], "b": "3"}
d3 = {"a": [1, 2], "b": "3"}
d1 = json.dumps(d1)
d2 = json.dumps(d2)
d3 = json.dumps(d3)
p1 = Person("abc", d1)
p2 = Person("abc", d2)
print(p1 is p2)
p3 = Person("abc", d3)
print(p2 is p3)
def test2():
p1 = Person("abc")
p2 = Person("abc")
print(p1 is p2)
p2.set_last_name("def")
print(p1 is p2)
if __name__ == '__main__':
test2()
输出
person initializing
True
True
输出一切都是我们所期望的,而且不管是类方法还是普通的函数,都可以使用lru_cache来实现对象方法的缓存,此外lru_cache还支持缓存大小限制(maxsize)和严格的参数类型匹配校验(typed)以及提供被装饰的对象方法以缓存的清除和信息查看等方法
@lru_cache
def function():
print("call function")
return 123
def test3():
f = function()
f2 = function()
print(function.cache_parameters())
print(function.cache_info())
function.cache_clear()
print(function.cache_info())
if __name__ == '__main__':
test3()
输出
call function
CacheInfo(hits=1, misses=1, maxsize=128, currsize=1)
{'maxsize': 128, 'typed': False}
CacheInfo(hits=0, misses=0, maxsize=128, currsize=0)
|