Python lru_cache实现

时间:2018-02-23 04:58:18

标签: python python-3.x

我试图理解python(3)中lru_cache decorator的实现,具体说明如何从函数key为其内部{{1}创建args }。

我一直在阅读https://github.com/python/cpython/blob/master/Lib/functools.py#L414

我无法理解为什么函数中有dict。我看到只有kwd_mark = (object(),)的{​​{1}}只有一个tuple的实例,它看起来像是所有object()args之间的分隔符函数调用的kwargs/kwds。 我试图了解它是否在这里做了一些特别的事情https://repl.it/repls/ExpensiveFinishedSandboxes,但我想不出任何事情。

1 个答案:

答案 0 :(得分:1)

链接很好,但是它们有破坏的倾向,所以我会尝试将所有内容直接包含在答案中。我们有这样的功能(简化):

def make_key(args, kwds, kwd_mark = (object(),)):
    key = args
    if kwds:
        key += kwd_mark
        for item in kwds.items():
            key += item
    ...
    return key

那里的评论颇具描述性:

  

从< ...>制作缓存密钥位置和关键字参数。密钥以尽可能平面的方式构建,而不是作为需要更多内存的嵌套结构。

让我们尝试一些示例运行:

>>> print(make_key((1, 2, 3), dict()))
(1, 2, 3)
>>> print(make_key((1, 2, 3), dict(a='x', b='y')))
(1, 2, 3, <object object at 0x7f6faf99d0c0>, 'a', 'x', 'b', 'y')
>>> print(make_key((1, 2, 4), dict(b='y', a='x')))
(1, 2, 4, <object object at 0x7f6faf99d0c0>, 'b', 'y', 'a', 'x')

结果是一个可散列的元组,表示用户将其user_function称为的参数。如您所述,kwd_mark在args和kwargs之间用作 sentinel值,因此以下调用将单独缓存:

user_function(1, 2, 3, 'a', 'x')
user_function(1, 2, 3, a='x')

我们本可以使用None作为分隔符,但是如果有人调用带有None的函数作为参数呢?虽然object()是Python中基类的一个对象,但它本身并没有做太多的事情,它的 unicity 很有用。如果您的分隔符是独一无二的,则不能将参数与分隔符混淆!并且因为这个sentinel是在函数定义时创建的,所以它会在程序运行时保持不变(请注意上面的结果引用相同的<object object at 0x7f6faf99d0c0>),因此它的散列将是相同的。

你可能会问,为什么不把它kwd_mark=object()?我认为这里的原因是它只用于将它“附加”到一个元组,所以否则你每次都必须创建一个新的元组(记住,元组是不可变的):

def make_key(args, kwds, kwd_mark=object()):
    key = args
    if kwds:
        key += (kwd_mark, )  # new tuple
        for item in kwds.items():
            key += item
相关问题