Python缓存类实例

本篇文章的内容主要包含

  1. 利用Python弱引用存储字典缓存类的实例,让参数相同的实例不用重复生成
  2. 略过复杂的通用化代码编写,利用Python自带库来缓存实例和方法对象

在Python的许多库中都有缓存实例的例子,比如logging模块的Logger类实例

import logging


a = logging.getLogger("abc")
b = logging.getLogger("abc")

print(a is b)   # True

弱引用(weakref)通常用于缓存或映射数据量较大的对象,当你使用python字典存放例如key为name,value为一个很大的图像对象,或者反过来,引用和修改这个图像对象时始终是“实时的”,因为它实际的存在字典中。但是利用弱引用中的WeakKeyDictionaryWeakValueDictionary,你引用这个图像时只需要使用它的名字即可,它不需要占用很大的空间来存储,实际上弱引用就是一个对象指针。

创建实例缓存的一种方式是写一个方法,每次都通过这个方法访问实例

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__':
    # test()
    # test1()
    test2()

输出

person initializing
True
True

输出一切都是我们所期望的,而且不管是类方法还是普通的函数,都可以使用lru_cache来实现对象方法的缓存,此外lru_cache还支持缓存大小限制(maxsize)和严格的参数类型匹配校验(typed)以及提供被装饰的对象方法以缓存的清除和信息查看等方法

@lru_cache
def function():
    print("call function")
    return 123


def test3():
    # 只会调用一次function
    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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值