在Python中创建函数参数的签名

2024-04-23 17:37:29 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个函数需要很长时间,需要能够缓存自己的结果,以便在使用相同参数再次调用时使用。下面的例子似乎解决了这个问题。我使用的是python3.6

我的问题围绕这一行:

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

Tags: 函数format参数paramrandombecallanswers
2条回答

通过使用*args(提供位置参数列表)或**kwargs(一个“关键字”参数的dict)语法提供参数,您可以按确定的顺序访问参数。考虑:

>>> def fn(*args):
...     for i in args: print(i)
... 
>>> fn('a', 'b', 'c')
a
b
c

或者

>>> def fn2(**kwargs):
...     for k in kwargs.keys(): print("{}: {}".format(k, kwargs[k]))
... 
>>> fn2(a='aval', b='bval', c='cval')
a: aval
c: cval
b: bval

要在缓存中使用这些参数,需要将参数转换为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'}

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_cache与您的情况相匹配,但它并不是最佳匹配,也不是所有情况下都完美的—无论如何,您最好安装第三方软件包进行缓存,或者滚动自己的缓存,但保持逻辑独立。你知道吗

可以应用于系统中各种类似函数或方法的特定编程逻辑的思想被称为"aspect oriented programing",Python decorator语法是一种廉价使用它的好方法。你知道吗

除了将逻辑与函数分离,并使用repr(locals())来序列化参数之外,您的方法是正确的:保留一个(模块)全局字典,其中包含每组参数的结果是缓存函数的常用方法。lru_cache只是以一种透明的方式发生的。你知道吗

相关问题 更多 >