记住Python中的默认kwarg

这是您可以记住Python函数的方法:

def memo_square(a, cache={}): if a not in cache: cache[a] = a*a return cache[a] 

接待是鲜为人知的,因此在削减范围内,我们将分析其工作原理和用途。

首先,它如何以及为什么起作用。 memo_square (与其他任何函数一样)是函数类的对象,除其他属性外,还创建了创建对象时会填充的memo_square.__defaults__元组。 首先,它包含一个空字典,如函数头所示:

 >>> memo_square.__defaults__ ({},) 

__defaults__是一个常规元组,您不能更改其元素。 是的,您可以一次替换整个默认值集,但只能替换为另一个元组:

 >>> def test(a=1, b=2): ... print(a, b) ... >>> test.__defaults__ (1, 2) >>> test() 1 2 >>> test.__defaults__ = (', ', '') >>> test() ,  >>> test.__defaults__[1] = '' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment >>> test.__defaults__ = {0: ', ', 1: ''} Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __defaults__ must be set to a tuple object 

Soryan,本文不会涉及Picaba。 好吧,这并不重要。 重要的是,除了非常狡猾的代码外, func.__defaults__在程序运行期间连同其所有元素一起创建了一次。 元组及其元素不会在每次函数调用时都被重新创建,只要存在该函数,就将使用它们。 但是要改变,如果元素本身是可变的,没有人会禁止它们。 无法使用此类元素是用python射击自己的最常见方法之一 。 但是实际上在函数调用之间保存值可能非常有用。 几次调用后, memo_square.__defaults__将如下所示:

 >>> memo_square(2) 4 >>> memo_square.__defaults__ ({2: 4},) >>> memo_square(5) 25 >>> memo_square.__defaults__ ({2: 4, 5: 25},) >>> memo_square(2) 4 >>> memo_square.__defaults__ ({2: 4, 5: 25},) 

如果已经为相同的值调用了该函数,则将计算该值,并且因此不补充高速缓存。 对于一个正方形,好处很小(严格来说,对于一个正方形,好处是负面的,因为在字典中搜索比将两个数相乘更昂贵),但是对于真正昂贵的函数,记忆/缓存可能会有用。 当然,您可以通过多种方式在python中提供它。 以下是我们的替代方案:

  • @ functools.lru_cache 。 functools模块中的装饰器,可以记住最近的函数调用。 它可靠且简单,但是它使用函数的所有参数作为键,这意味着它需要哈希,并且不能注意到两个形式上不同的参数值是等效的。 首先,关于集合中的函数的所有内容都是清楚的,例如,您可能会忘记。 好吧,或者在打电话时,将它们转换为Frozenset。 例如,对于第二个,我有一个函数,该函数将一个SQL连接和一个数字作为输入,并对与该数字关联的数据进行某种处理。 在程序运行期间,很可能会断开连接并重新建立连接,然后lru_cache缓存将飞走。 但是他知道如何仅缓存有限数量的调用(避免内存泄漏),并且有据可查。
  • 缓存外部功能:

     def square(a): return a**a cache = {} for x in values: if x not in cache: cache[x] = x**x print cache[x] 

    意思是一样的,但是麻烦得多。 此外,缓存变量在函数外部是可见的,尽管除了用于记忆以外,它不用于其他任何用途。 带有default参数的备注存储过程中的缓存只能通过func.__defaults__从外部访问,这很难被错误地访问。
  • 使用缓存刷新完整的对象,并将其功能用作方法。 良好的体系结构和可测试性,它使您可以维护任意复杂的缓存逻辑,但是由于目标代码中的样板文件,因此更加麻烦。 另外,如果存在多个可记忆的函数,则不清楚要继承什么,是否要继承。

这种记忆方法所失去的主要优点是它不是很惯用。 就个人而言,当我第一次偶然发现这个决定时,我花了几分钟思考一下这里发生了什么以及为什么。 另一方面,在这几分钟内,我开始更好地了解Python函数及其参数的排列方式。 因此,即使您不使用默认参数(例如用于记忆或加快名称解析速度 ),了解此技术对于任何营养学家仍然有用。

Source: https://habr.com/ru/post/zh-CN426341/


All Articles