在Python中创建函数参数的签名
我有一个函数需要很长时间,并且需要能够缓存它自己的结果,以便在使用相同参数再次调用它时使用。请参阅下面的示例,该示例似乎解决了问题。我正在使用Python 3.6 我的问题围绕这一行:在Python中创建函数参数的签名,python,python-3.x,function,parameters,Python,Python 3.x,Function,Parameters,我有一个函数需要很长时间,并且需要能够缓存它自己的结果,以便在使用相同参数再次调用它时使用。请参阅下面的示例,该示例似乎解决了问题。我正在使用Python 3.6 我的问题围绕这一行: param_sig = repr(locals()) 1) 是否有一种更类似python的方法来获得传递给函数的参数的唯一签名 2) 我可以依赖Python将函数参数插入到locals()映射中的顺序吗?同样,这似乎可行,但如果需要,我可以在一个不太优雅的签名创建者中显式地重新列出每个参数,如: param_s
param_sig = repr(locals())
1) 是否有一种更类似python的方法来获得传递给函数的参数的唯一签名
2) 我可以依赖Python将函数参数插入到locals()映射中的顺序吗?同样,这似乎可行,但如果需要,我可以在一个不太优雅的签名创建者中显式地重新列出每个参数,如:
param_sig = "{},{},{}".format(a,b,c)
示例代码:
import random
cached_answers = {}
def f(a=1, b=2, c=3):
param_sig = repr(locals())
if param_sig in cached_answers:
ans = cached_answers[param_sig]
print("Call: {} = CACHED {}".format(param_sig,ans))
return ans
else:
# do heavy lifting then cache the result
ans = random.random()
print("Call: {} = {}".format(param_sig,ans))
cached_answers[param_sig] = ans
return ans
# various calls... some of which are repeated and should be cached
f()
f(b=9)
f(c=9, a=9)
f() # should be cached
parms={'a':9}
f(**parms)
f(b=9) # should be cached
f(a=9) # should be cached
repr(locals())
非常糟糕,因为它不适用于此函数。它会起作用,或者可以让它起作用,我可以深入研究一些问题,而不是这样做的语义
我将回到问题上来-首先,您的解决方案:
Python有一个缓存函数,就像您在stdlib上的functools
模块中需要的一样-只需导入它并将其用作装饰器:
from functools import lru_cache
@lru_cache()
def f(a=1, b=2, c=3):
# just put the heavy stuff inside your original "else" clause here
现在,我们来理解为什么decorators有一个更好的解决方案:缓存结果的逻辑与函数的逻辑完全不混合,而且,缓存所使用的任何方法都可以用于缓存程序中的任何函数—无需在每个函数体中复制缓存代码
您将了解到,虽然Python自己的lru\u缓存
与您的情况相匹配,但它并不是最佳匹配,也不是所有情况下的完美匹配-无论如何,您最好安装第三方缓存包或滚动您自己的缓存,但保持逻辑分离
可以应用于系统中各种类似函数或方法的特定编程逻辑的概念称为,而Python decorator语法是一种廉价使用它的好方法
除了将逻辑与函数分离,并使用
repr(locals())
序列化参数之外,您的方法是正确的:为每组参数保留一个(模块)全局字典是缓存函数的常用方法。lru\u缓存
恰好以一种透明的方式对其进行处理。通过使用*args(提供位置参数列表)或**kwargs(一种“关键字”参数的dict)语法提供参数,您可以以确定的顺序访问参数。考虑:
>>> def fn(*args):
... for i in args: print(i)
...
>>> fn('a', 'b', 'c')
a
b
c
或
要在缓存中使用这些参数,需要将args转换为dict键。
我建议对缓存键使用元组,而不是将其格式化为字符串。元组是不可变的,因此可以对它们进行散列(dict的一个要求)。例如:
>>> cache=dict()
>>> def fn3(**kwargs):
... key=(kwargs['a'], kwargs['b'], kwargs['c'])
... val = ':'.join( (kwargs['a'], kwargs['b'], kwargs['c']) )
... print(str(key))
... print(val)
... cache[key] = val
... return
...
>>> fn3(a='1', b='2', c='3')
('1', '2', '3')
1:2:3
>>> print(str(cache))
{('1', '2', '3'): '1:2:3'}
…为什么不用?谢谢,杰夫。这似乎是假设所有参数都没有默认值,并更改了函数代码,因为kwargs参数在函数中不能作为局部变量使用。我喜欢使用元组作为键的想法。谢谢。我的特定问题(获取参数签名)的答案在lru_缓存代码中。正如您所猜测的,我的实际问题比缓存示例要复杂一些,但我没有考虑使用装饰器,这似乎是正确的解决方案。
>>> cache=dict()
>>> def fn3(**kwargs):
... key=(kwargs['a'], kwargs['b'], kwargs['c'])
... val = ':'.join( (kwargs['a'], kwargs['b'], kwargs['c']) )
... print(str(key))
... print(val)
... cache[key] = val
... return
...
>>> fn3(a='1', b='2', c='3')
('1', '2', '3')
1:2:3
>>> print(str(cache))
{('1', '2', '3'): '1:2:3'}