Class 类 中方法的装饰器

Class decorators for methods in classes

class class 中方法的装饰器如何工作?这是我通过一些实验所做的示例:

from functools import wraps


class PrintLog(object):

    def __call__(self, func):
        @wraps(func)
        def wrapped(*args):
            print('I am a log')
            return func(*args)
        return wrapped


class foo(object):
    def __init__(self, rs: str) -> None:
        self.ter = rs

    @PrintLog()
    def baz(self) -> None:
        print('inside baz')


bar = foo('2')
print('running bar.baz()')
bar.baz()

这非常好用。然而,我的印象是装饰器不需要用 () 调用,但是当我从 @PrintLog() 中删除括号时,我得到这个错误:

    def baz(self) -> None:
TypeError: PrintLog() takes no arguments

我是不是有什么地方missing/do不明白?我也试过用 __init__() 传递一个一次性 arg,它起作用了。

class PrintLog(object):

    def __init__(self, useless):
        print(useless)

    def __call__(self, func):
        @wraps(func)
        def wrapped(*args):
            print('I am a log')
            return func(*args)
        return wrapped

class foo(object):
    def __init__(self, rs: str) -> None:
        self.ter = rs

    @PrintLog("useless arg that I'm passing to __init__")
    def baz(self) -> None:
        print('inside baz')

同样,这可行,但我不想将任何参数传递给装饰器。

tl;dr: This question 在 python 3.x.

感谢帮助!

Class 装饰器接受函数作为 __init__ 方法中的主题(因此是日志消息),因此您的装饰器代码应如下所示:

class PrintLog(object):

    def __init__(self, function):
        self.function = function

    def __call__(self):
        @wraps(self.function)
        def wrapped(*args):
            print('I am a log')
            return self.function(*args)
        return wrapped

抱歉,如果这不起作用,我会在我的移动设备上回答。

编辑:

好吧,这可能不是你想要的,但这是实现它的方法:

from functools import update_wrapper, partial, wraps


class PrintLog(object):

    def __init__(self, func):
        update_wrapper(self, func)
        self.func = func
    
    def __get__(self, obj, objtype):
        """Support instance methods."""
        return partial(self.__call__, obj)

    def __call__(self, obj, *args, **kwargs):
        @wraps(self.func)
        def wrapped(*args):
            print('I am a log')
            return self.func(*args)
        return wrapped(obj, *args)


class foo(object):
    def __init__(self, rs: str) -> None:
        self.ter = rs

    @PrintLog
    def baz(self) -> None:
        print('inside baz')


bar = foo('2')
print('running bar.baz()')
bar.baz()

装饰器必须定义 __get__ 方法,因为您要将装饰器应用于实例方法。描述符如何具有 foo 实例的上下文?

参考:Decorating Python class methods - how do I pass the instance to the decorator?

你错过了一张大图。

@decorator
def foo(...):
   function_definition

几乎相同(除了一些内部修改)
temp = foo
foo = decorator(temp)

装饰器是什么并不重要,只要它能像一个函数一样。

你的例子相当于:

baz = PrintLog("useless thing")(<saved defn of baz>)

由于 PrintLog 是 class,PrintLog(...) 创建了 PrintLog 的一个实例。该实例有一个 __call__ 方法,因此它可以像一个函数一样工作。

一些装饰器被设计成接受参数。一些装饰器被设计成不接受参数。有些,比如 @lru_cache,是 Python 魔法的一部分,它查看“参数”是一个函数(因此装饰器被直接使用)还是一个 number/None,以便它returns 一个函数,然后成为装饰器。