python - 装饰一个已经是类方法的方法?

标签 python decorator chaining

今天早上我遇到了一个有趣的问题。我有一个看起来像这样的基类:

# base.py
class Base(object):

    @classmethod
    def exists(cls, **kwargs):
        # do some work
        pass

还有一个看起来像这样的装饰器模块:

# caching.py

# actual caching decorator
def cached(ttl):
    # complicated

def cached_model(ttl=300):
    def closure(model_class):
        # ...
        # eventually:
        exists_decorator = cached(ttl=ttl)
        model_class.exists = exists_decorator(model_class.exists))

        return model_class
    return closure

这是我的子类模型:

@cached_model(ttl=300)
class Model(Base):
    pass

问题是,当我实际调用 Model.exists 时,我收到关于参数数量错误的投诉!检查装饰器中的参数没有发现任何奇怪的事情 - 参数正是我所期望的,并且它们与方法签名匹配。如何向已使用 classmethod 装饰的方法添加更多装饰器?

并非所有模型都被缓存,但是 exists() 方法作为类方法存在于每个模型中,因此重新排序装饰器不是一个选项:cached_model 可以将类方法添加到 exists( ),那么是什么让 exists() 成为未缓存模型上的类方法呢?

最佳答案

在 Python 中,当一个方法被声明时,在一个函数体中,它就像一个函数—— 一旦类被解析并存在,通过“.”检索方法。运算符将该函数 - 即时 - 转换为方法。此转换确实将第一个参数添加到方法中(如果它不是静态方法)-

所以:

>>> class A(object):
...    def b(self):
...        pass
... 
>>> A.b is A.b
False

因为每次检索“A”的“b”属性都会产生“方法对象 b”的不同实例

>>> A.b
<unbound method A.b>

原始函数“b”可以在不进行任何转换的情况下检索

>>> A.__dict__["b"]
<function b at 0xe36230>

对于用 @classmethod 修饰的函数,同样的情况会发生,当从 A 中检索值“class”时,它会被添加到参数列表中。

@classmethod@staticmethod 装饰器会将底层函数包装在与普通实例方法不同的描述符中。一个类方法对象——当它被 classmethod 包装时,函数变成了一个描述符对象,它有一个“__get__”方法,该方法将返回一个包装底层函数的函数——并添加“cls "参数在所有其他参数之前。

@classmethod 的任何进一步装饰器都必须“知道”它实际上是在处理描述符对象,而不是函数。 -

>>> class A(object):
...    @classmethod
...    def b(cls):
...       print b
... 
>>> A.__dict__["b"]
<classmethod object at 0xd97a28>

因此,让 @classmethod 装饰器成为最后一个应用于该方法的装饰器(堆栈中的第一个装饰器)要容易得多 - 这样其他装饰器就可以继续工作一个简单的函数(知道“cls”参数将作为第一个插入)。

关于python - 装饰一个已经是类方法的方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8977359/

相关文章:

python - python中与append()函数相反的是什么?

python - 如何使用 Selenium 和 Python 定位元素并提取内部文本

python - Python 中的属性装饰器和设置函数

java - 在java中组合比较器

python - 对 virtualenv 使用单个站点包(作为异常(exception))

Python类接口(interface)设计

Python- dill : Can't pickle decorated class

python - python装饰器的奇怪语法规范

javascript - 返回带有链接模式 javascript 的项目

javascript - 如何在 jQuery 中构造一个新的 HTML 元素?