我试图理解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,但我想不出任何事情。
答案 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