当前位置: 首页 > 面试题库 >

使用类作为方法装饰器

吕扬
2023-03-14
问题内容

尽管有很多关于将类用作装饰器的资源,但我找不到能处理装饰 方法
问题的资源。这个问题的目的是解决这个问题。我将发布自己的解决方案,但是当然也邀请其他所有人发布他们的解决方案。

标准装饰器类实现的问题是python不会创建装饰函数的绑定方法:

class Deco:
    def __init__(self, func):
        self.func= func

    def __call__(self, *args):
        self.func(*args)

class Class:
    @Deco
    def hello(self):
        print('hello world')

Class().hello() # throws TypeError: hello() missing 1 required positional argument: 'self'

方法装饰者需要克服这一障碍。

要求

以前面示例中的类为例,预期将完成以下工作:

>>> i= Class()
>>> i.hello()
hello world
>>> i.hello
<__main__.Deco object at 0x7f4ae8b518d0>
>>> Class.hello is Class().hello
False
>>> Class().hello is Class().hello
False
>>> i.hello is i.hello
True

理想情况下,函数__doc__和签名以及类似属性也应保留。


问题答案:

通常,当方法访问为时some_instance.some_method(),python的描述符协议会加入并调用some_method.__get__(),这会返回绑定的方法。但是,由于该方法已被Deco类的实例替换,因此不会发生-
因为Deco它不是描述符。为了Deco按预期进行工作,它必须实现一个__get__返回自身绑定副本的方法。

实作

这是基本的“不执行任何操作”修饰符类:

import inspect
import functools
from copy import copy


class Deco(object):
    def __init__(self, func):
        self.__self__ = None # "__self__" is also used by bound methods

        self.__wrapped__ = func
        functools.update_wrapper(self, func)

    def __call__(self, *args, **kwargs):
        # if bound to an object, pass it as the first argument
        if self.__self__ is not None:
            args = (self.__self__,) + args

        #== change the following line to make the decorator do something ==
        return self.__wrapped__(*args, **kwargs)

    def __get__(self, instance, owner):
        if instance is None:
            return self

        # create a bound copy
        bound = copy(self)
        bound.__self__ = instance

        # update __doc__ and similar attributes
        functools.update_wrapper(bound, self.__wrapped__)

        # add the bound instance to the object's dict so that
        # __get__ won't be called a 2nd time
        setattr(instance, self.__wrapped__.__name__, bound)

        return bound

要使装饰器执行某些操作,请在__call__方法中添加代码。

这是一个带有参数的参数:

class DecoWithArgs(object):
    #== change the constructor's parameters to fit your needs ==
    def __init__(self, *args):
        self.args = args

        self.__wrapped__ = None
        self.__self__ = None

    def __call__(self, *args, **kwargs):
        if self.__wrapped__ is None:
            return self.__wrap(*args, **kwargs)
        else:
            return self.__call_wrapped_function(*args, **kwargs)

    def __wrap(self, func):
        # update __doc__ and similar attributes
        functools.update_wrapper(self, func)

        return self

    def __call_wrapped_function(self, *args, **kwargs):
        # if bound to an object, pass it as the first argument
        if self.__self__ is not None:
            args = (self.__self__,) + args

        #== change the following line to make the decorator do something ==
        return self.__wrapped__(*args, **kwargs)

    def __get__(self, instance, owner):
        if instance is None:
            return self

        # create a bound copy of this object
        bound = copy(self)
        bound.__self__ = instance
        bound.__wrap(self.__wrapped__)

        # add the bound decorator to the object's dict so that
        # __get__ won't be called a 2nd time
        setattr(instance, self.__wrapped__.__name__, bound)
        return bound

这样的实现使我们可以在方法和函数上使用装饰器,因此我认为应该将其视为良好实践。



 类似资料:
  • 问题内容: 考虑这个小例子: 哪个打印 为什么参数(应该是Test obj实例)没有作为第一个参数传递给装饰函数? 如果我手动进行操作,例如: 它按预期工作。但是,如果我必须事先知道某个函数是否装饰,它就破坏了装饰器的全部目的。这里的模式是什么,还是我误会了什么? 问题答案: tl; dr 您可以通过将类作为描述符并返回部分应用的函数来解决此问题,该函数从中应用对象作为参数之一,如下所示 实际问题

  • 问题内容: 我正在尝试编写一个使用装饰器函数的python类,该装饰器函数需要实例状态的信息。这可以按预期工作,但是如果我将装饰器明确设为staticmetod,则会出现以下错误: 为什么? 这是代码: 而且,如果我只删除该行,一切都会正常,但是我不明白为什么。它不应该作为第一个论点吗? 问题答案: 这不是应该使用的方式。 对象是返回包装对象的描述符,因此它们仅在以身份访问时才起作用。例 版画 在

  • 问题内容: 我希望能够创建一个python装饰器,该装饰器会在全局存储库(具有某些属性)中自动“注册”类方法。 示例代码: 我希望加载完成后,某处将包含以下内容: 这可能吗? 问题答案: 不只是装饰,不。但是,元类可以在创建类后自动使用它。如果您的装饰器仅记录了元类应该做什么,则可以执行以下操作: 印刷

  • 本文向大家介绍基于Python 装饰器装饰类中的方法实例,包括了基于Python 装饰器装饰类中的方法实例的使用技巧和注意事项,需要的朋友参考一下 title: Python 装饰器装饰类中的方法 comments: true date: 2017-04-17 20:44:31 tags: ['Python', 'Decorate'] category: ['Python'] --- 目前在中文网

  • 问题 你想给类或静态方法提供装饰器。 解决方案 给类或静态方法提供装饰器是很简单的,不过要确保装饰器在 @classmethod 或 @staticmethod 之前。例如: import time from functools import wraps # A simple decorator def timethis(func): @wraps(func) def wrapp

  • 问题内容: 我有一堂课,他们的功能上有一个乏味的重复模式,我想把这种模式变成装饰器。但事实是,此装饰器必须访问当前实例的某些属性,因此我想将其转换为此类中的方法。我有一些问题。 因此,这类似于我想要的: (1)这是问题所在。我想使用该方法来装饰该方法。但是我不知道如何使用当前实例中的方法作为装饰器。 有没有简单的方法可以做到这一点? 编辑:好的,答案很明显。正如Sven所说的那样,装饰器本身只是在