首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >我的lru_cache实现

我的lru_cache实现
EN

Code Review用户
提问于 2017-06-22 10:06:40
回答 1查看 746关注 0票数 6

这段代码的功能与functools.lru_cache完全一样。在设计、实现、风格或其他方面有什么可以改进的地方吗?(Python版本= 3.6.*)

代码语言:javascript
复制
from collections import OrderedDict
from functools import wraps

def lru_cache(maxsize=None):
    def decorator(func):

        cache = OrderedDict()
        @wraps(func)
        def decorated(*arg, **kwargs):
            key = (*arg, None, *kwargs) 
            try:
                val = cache[key]
                del cache[key]            
            except KeyError:
                val = func(*arg, **kwargs)

            cache[key] = val
            if maxsize and len(cache) > maxsize:
                cache.popitem(last=False)

            return val

        return decorated
    return decorator
EN

回答 1

Code Review用户

回答已采纳

发布于 2017-06-22 12:08:48

对于如何处理kwargs,您有一个相当大的错误。

  1. 您不关心值,只关心键的存在。
  2. 你关心关键字的顺序。
  3. 您不关心默认关键字。

为了测试这一点,我们可以创建一个简单的函数来查看您的密钥是什么。做一些简单的测试。

代码语言:javascript
复制
def test(*args, **kwargs):
    return (*args, None, *kwargs)

print(test(a=1)) # (None, 'a')
print(test(a=2)) # (None, 'a')
print(test(a=1, b=2)) # (None, 'a', 'b')
print(test(b=2, a=1)) # (None, 'b', 'a')

因此,您应该对kwargs.items()进行排序。然而,要正确地将argskwargs更改为函数所采用的单一标准,则要复杂得多。最简单的方法是使用inspect.signature获取以下内容:

代码语言:javascript
复制
def test(fn):
    signature = inspect.signature(fn)
    def call(*args, **kwargs):
        bind = signature.bind(*args, **kwargs)
        bind.apply_defaults()
        return bind.args, tuple(sorted(bind.kwargs.items()))
    return call

def fn1(a, b, c=1, *, d=2, **kwargs):
    pass

def fn2(a, b, *args, c=1, d=2, **kwargs):
    pass

# Checking that it works as intended
for fn in (fn1, fn2):
    t = test(fn)
    print(t('a', 'b'))
    print(t('a', 'b', 'c'))
    print(t(a='a', b='b', c='c'))
    print(t(b='b', c='c', a='a'))
    print(t('a', 'b', d='d'))
    print(t('a', 'b', test='test'))
    print(t('a', 'b', t1='t1', t2='t2'))
    print(t('a', 'b', t2='t2', t1='t1'))

所以我会用:

代码语言:javascript
复制
from collections import OrderedDict
from functools import wraps
from inspect import signature

def lru_cache(maxsize=None):
    def decorator(func):
        func_sig = signature(func)
        cache = OrderedDict()
        @wraps(func)
        def decorated(*arg, **kwargs):
            bind = func_sig.bind(*args, **kwargs)
            bind.apply_defaults()
            args, kwargs = bind.args, bind.kwargs
            key = (arg, tuple(sorted(kwargs.items())))
            try:
                val = cache[key]
                del cache[key]
            except KeyError:
                val = func(*arg, **kwargs)

            cache[key] = val
            if maxsize and len(cache) > maxsize:
                cache.popitem(last=False)

            return val

        return decorated
    return decorator

添加typed也相当容易,可以归结为使用type的额外值生成更多的元组。我敢肯定你有能力这么做。

票数 5
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/166388

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档