【问题标题】:Class decorators for methods in classes类中方法的类装饰器
【发布时间】:2022-01-06 21:55:02
【问题描述】:

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

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

有什么我遗漏/不明白的地方吗?我也试过用__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 中。

帮助表示赞赏!

【问题讨论】:

标签: python python-3.x class decorator class-decorator


【解决方案1】:

类装饰器接受函数作为 __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?

【讨论】:

  • 它没有。 I am a log 不打印,inside baz 也不打印
  • 是的,这行得通——我确实阅读了一些关于__get__ 方法的信息,所以我想我现在明白了。谢谢!
【解决方案2】:

你错过了一张大图。

@decorator
def foo(...):
   function_definition

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

装饰器是什么并不重要,只要它可以像函数一样工作。

你的例子相当于:

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

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

一些装饰器被设计为接受参数。一些装饰器被设计成不接受参数。有些,比如@lru_cache,是 Python 魔术的一部分,它查看“参数”是函数(因此直接使用装饰器)还是数字/无,因此它返回一个函数,然后成为装饰器.

【讨论】:

  • 那么你是说类装饰器需要() 来实例化它们吗?还是我应该做类似PL = PrintLog(); @PL 的事情?
  • 没有。我是说你需要在使用装饰器之前阅读它的文档,而不是做任何假设。一些装饰器是函数,不带参数。有些是带有__call__ 方法的类并且确实需要参数。
  • 好的。我想我明白为什么我现在遇到这个问题了,但是有没有对装饰器等有用的文档?
  • 在您最喜欢的搜索引擎中查找“Python 装饰器”,您会在任何级别找到各种信息。
猜你喜欢
  • 2014-01-14
  • 1970-01-01
  • 2020-01-11
  • 2020-09-03
  • 1970-01-01
  • 1970-01-01
  • 2012-02-09
  • 2012-09-11
  • 1970-01-01
相关资源
最近更新 更多