使用类作为方法装饰器 [英] Using classes as method decorators

查看:109
本文介绍了使用类作为方法装饰器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

虽然有关于使用类作为装饰器的大量资源,但我没有能够找到解决装饰方法问题的方法。这个问题的目的是解决这个问题。我将发布自己的解决方案,但是当然也邀请其他所有人发布他们的解决方案。




实现不起作用


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

  class Deco:
def __init __(self,func):
self.func = func

def __call __(self,* args):
self.func(* args)

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

Class()。hello()#引发TypeError:hello()缺少1个必需的位置参数:'self'

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




要求


从上一个类中学习例如,预计以下各项将起作用:

 >> i = Class()
>> i.hello()
hello world
>> i.hello
< __ main __。Deco object at 0x7f4ae8b518d0>
>> Class.hello是Class()。hello
False
>>> Class()。hello是Class()。hello
False
>>> i.hello是i.hello
True

理想情况下,该函数的 __ doc __

解决方案

通常以<$ c身份访问方法时$ c> some_instance.some_method(),python的描述符协议插入并调用 some_method .__ get __(),这将返回一个绑定方法。但是,由于该方法已替换为 Deco 类的实例,因此不会发生-因为 Deco 是不是描述符。为了使 Deco 正常工作,它必须实现 __ get __ 方法,该方法返回自身的绑定副本。 / p>

实现



以下是基本的什么都不做修饰符类:

 导入检查
导入功能工具
从副本导入副本


类Deco(object):
def __init __(self,func):
self .__ self__ = None#绑定方法

self .__ wrapped__ = func
functools.update_wrapper(self ,func)

def __call __(self,* args,** kwargs):
#如果绑定到对象,则将self作为第一个参数
传递,如果self .__ self__是不是无:
args =(self .__ self__,)+ args

#==更改以下行以使装饰器执行某些操作==
返回self .__ wrapped __(* args,** kwargs)

def __get __(self,instance,owner):
如果instance为No ne:
返回自身

#创建绑定副本
bound =复制(self)
bound .__ self__ =实例

#更新__doc__和类似属性
functools.update_wrapper(bound,self .__ wrapped__)

#将绑定实例添加到对象的字典中,这样
#__get__不会被称为第二个时间
setattr(实例,self .__ wrapped __.__ name__,绑定)

返回绑定

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






这里有一个参数:

  class DecoWithArgs(object):
#==更改构造函数的参数以满足您的需求==
def __init __(self,* args):
self.args = args

self .__ wrapped__ =无
self .__ self__ = None

def __call __(self,* args,** kwargs):
如果self .__ wrapped__为None:
返回self .__ wrap(* args,** kwargs)
else:
return self .__ call_wrapped_function(* args,** kwargs)

def __wrap(self,func):
#更新__doc__和类似属性
functools.update_wrapper(self,func)

return self

def __call_wrapped_function(self,* args,** kwargs):
#如果绑定到一个对象,如果self .__ self__不是None,则将其作为第一个参数
传递:
args =( self .__ self__,)+ args

#==更改以下行以使装饰器执行某些操作==
返回self .__ wrapped __(* args,** kwargs)

def __get __(self,instance,owner):
,如果实例为None:
return self

#创建该对象的绑定副本
=复制(self)
边界.__ self__ =实例
边界.__ wrap(self .__ wrappe d__)

#将绑定的装饰器添加到对象的字典中,以使
#__get__不会被第二次调用
setattr(instance,self .__ wrapped __.__ name__,bound )
返回界限

通过这样的实现,我们可以在方法以及功能,因此我认为应该将其视为一种良好做法。


While there are plenty of resources about using classes as decorators, I haven't been able to find any that deal with the problem of decorating methods. The goal of this question is to fix that. I will post my own solution, but of course everyone else is invited to post theirs as well.


Why the "standard" implementation doesn't work

The problem with the standard decorator class implementation is that python will not create a bound method of the decorated function:

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'

A method decorator needs to overcome this hurdle.


Requirements

Taking the classes from the previous example, the following things are expected to work:

>>> 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

Ideally, the function's __doc__ and signature and similar attributes are preserved as well.

解决方案

Usually when a method is accessed as some_instance.some_method(), python's descriptor protocol kicks in and calls some_method.__get__(), which returns a bound method. However, because the method has been replaced with an instance of the Deco class, that does not happen - because Deco is not a descriptor. In order to make Deco work as expected, it must implement a __get__ method that returns a bound copy of itself.

Implementation

Here's basic "do nothing" decorator class:

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

To make the decorator do something, add your code in the __call__ method.


Here's one that takes parameters:

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

An implementation like this lets us use the decorator on methods as well as functions, so I think it should be considered good practice.

这篇关于使用类作为方法装饰器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆