繁体   English   中英

Python使用类似于模拟补丁的技术缓存内部调用

[英]Python cache internal calls using technique similar to mock patch

我想对API中的特定函数使用缓存。 我想通过使用类似于模拟补丁的技术来实现相同的功能,而不是逐行修改内部代码。 例如

@cache_patch('lib.Someobjclass.func1',ttl=200)
@cache_patch('lib.Someotherobjclass.func2',ttl=1000)
function abc(*args, **kwargs):
    '''do stuff'''
    cache1 = someobj.func1(args,kwargs)
    '''do more stuff'''
    cache2 = someotherobj.func2(args,kwargs)

有没有可以使用的库或技术?

假设

我并不是100%清楚你想要什么样的行为,所以我假设只在执行装饰器的函数执行期间才使用缓存。


必要的点点滴滴

你需要

  • 使用functools.lru_cache来实现缓存
  • 创建一个能够获取其他参数的装饰器
  • 使用importlib将作为第一个参数给出的类从string导入装饰器
  • 使用缓存版本修补所需方法

把它放在一起

import importlib
from functools import lru_cache


class Addition:
    def __init__(self, a):
        self.a = a

    def uncached_addition(self, b):
        # print is only used to demonstrate if the method is actually called or not
        print(f"Computing {self.a} + {b}")
        return self.a + b


class cache_patch:
    def __init__(self, method_as_str, ttl):
        # split the given path into module, class and method name
        class_as_str, method_name = method_as_str.rsplit(".", 1)
        module_path, class_name = class_as_str.rsplit(".", 1)

        self.clazz = getattr(importlib.import_module(module_path), class_name)
        self.method_name = method_name
        self.ttl = ttl

    def __call__(self, func):
        def wrapped(*args, **kwargs):
            # monkey patch the original method with a cached version
            uncached_method = getattr(self.clazz, self.method_name)
            cached_method = lru_cache(maxsize=self.ttl)(uncached_method)
            setattr(self.clazz, self.method_name, cached_method)

            result = func(*args, **kwargs)

            # replace cached method with original
            setattr(self.clazz, self.method_name, uncached_method)
            return result
        return wrapped


@cache_patch('__main__.Addition.uncached_addition', ttl=128)
def perform_patched_uncached_addition(a, b):
    d = Addition(a=1)
    print("Patched nr. 1\t", d.uncached_addition(2))
    print("Patched nr. 2\t", d.uncached_addition(2))
    print("Patched nr. 3\t", d.uncached_addition(2))
    print()


if __name__ == '__main__':
    perform_patched_uncached_addition(1, 2)
    d = Addition(a=1)
    print("Unpatched nr. 1\t", d.uncached_addition(2))
    print("Unpatched nr. 2\t", d.uncached_addition(2))
    print("Unpatched nr. 3\t", d.uncached_addition(2))

结果

从输出中可以看出,调用perform_patched_uncached_addition只会输出一次Computing 1 + 2 ,然后使用缓存结果:

Computing 1 + 2
Patched call nr. 1:  3
Patched call nr. 2:  3
Patched call nr. 3:  3

对此函数之外的类进行的任何调用都将使用该方法的未修补的非缓存版本:

Computing 1 + 2
Unpatched call nr. 1:    3
Computing 1 + 2
Unpatched call nr. 2:    3
Computing 1 + 2
Unpatched call nr. 3:    3

注意事项

如果您计划在多线程环境中使用此方法,您当然需要注意。 您将无法判断将应用缓存修补程序的时间。
此外,缓存是基于传递给方法的参数完成的,其中包括self 这意味着该类的每个实例都将拥有自己的“缓存”。


边注

与上面的代码不同, functools.lru_cache在大多数情况下用作装饰器:


class Addition:
    def __init__(self, a):
        self.a = a

    @lru_cache(maxsize=128)
    def cached_addition(self, b):
        print(f"Computing {self.a} + b")
        return self.a + b

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM