【问题标题】:How to apply a memorize decorator to an instance method?如何将记忆装饰器应用于实例方法?
【发布时间】:2018-12-10 19:16:00
【问题描述】:

我想在类方法上使用 memorize 装饰器。 cExample.pri() 打电话给self.text()memorize 似乎不知道self。当memorize 调用self.func(*key) 时,它缺少cExample obj,因此它抱怨缺少args。

如何更改这个 memorize 装饰器,以便它能够将调用者的 self 传递给函数?

Python3.5.2

class memorize(dict):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args):
        return self[args]

    def __missing__(self, key):
        result = self[key] = self.func(*key)
        return result

class cExample():
    @memorize
    def pri(self, text):
        return self.text(text)

    def text(self, text):
        return text

c = cExample()
print(c.pri('hi'))

输出:

Traceback (most recent call last):
  File "x.py", line 23, in <module>
    print(c.pri('hi'))
  File "x.py", line 7, in __call__
    return self[args]
  File "x.py", line 11, in __missing__
    result = self[key] = self.func(*key)
TypeError: pri() missing 1 required positional argument: 'text'

【问题讨论】:

  • 内置的functools.lru_cache似乎没有这个问题,可以考虑使用
  • memorize 内部访问cExample 的self 很棘手,因为通常的方法绑定过程只对函数对象自动起作用,而memorize 是一个字典,而不是一个函数。您可以通过覆盖 memorize 的 __get__ 来实现自己的方法绑定,但是 IMO 这样做非常复杂,以至于您失去了使您的原始想法首先具有吸引力的简洁性。到时候还不如写一个普通的基于函数的装饰器。
  • 您想要一个缓存来处理所有cExample 实例,还是希望每个实例都有自己的缓存?如果是前者,您是否希望缓存忽略self

标签: python python-3.x python-decorators


【解决方案1】:

您需要将self(即c)传递给cExample.pri(即self.func)。但是__missing__ 不允许你这样做:它只接收密钥。

你可以使用基于函数的装饰器重写它:

import functools

def memorize2(f):
    cache = {}
    @functools.wraps(f)
    def wrapper(*args):
        if args not in cache:
            cache[args] = f(*args)
        return cache[args]
    return wrapper

class cExample():
    @memorize2
    def pri(self, text):
        return self.text(text)

    def text(self, text):
        return text

c = cExample()
print(c.pri('hi'))  # hi

(我使用functools.wraps是为了不丢失装饰方法的原始名称)。

在这种方法中,self 将作为位置参数传递给 wrapper 并代理到 cExample.pri

【讨论】:

    猜你喜欢
    • 2011-05-24
    • 2019-02-03
    • 2017-11-12
    相关资源
    最近更新 更多