来自其他类的方法的类装饰器 [英] Class decorator for methods from other class

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

问题描述

注意:
我在这里有一个相关的问题:如何访问变量是从类装饰器中应用它的方法中获得的?

我打算编写一个相当复杂的装饰器.因此,装饰器本身应该是它自己的一类.我知道这在Python(Python 3.8)中是可能的:

I'm planning to write a fairly complicated decorator. Therefore, the decorator itself should be a class of its own. I know this is possible in Python (Python 3.8):

import functools

class MyDecoratorClass:
    def __init__(self, func):
        functools.update_wrapper(self, func)
        self.func = func
    
    def __call__(self, *args, **kwargs):
        # do stuff before
        retval = self.func(*args, **kwargs)
        # do stuff after
        return retval

@MyDecoratorClass
def foo():
    print("foo")

现在,当我尝试将装饰器应用于方法而不是仅仅应用函数时,我的问题就开始了-尤其是如果它是另一个类的方法时.让我告诉你我尝试过的事情:

Now my problem starts when I try to apply the decorator on a method instead of just a function - especially if it's a method from another class. Let me show you what I've tried:


下面的装饰器 MyDecoratorClass 不(或不应)执行任何操作.这只是样板代码,准备在以后使用.类 Foobar 中的方法 foo()打印在其上调用的对象:

The decorator MyDecoratorClass below doesn't (or shouldn't) do anything. It's just boilerplate code, ready to be put to use later on. The method foo() from class Foobar prints the object it is called on:

import functools

class MyDecoratorClass:
    def __init__(self, method):
        functools.update_wrapper(self, method)
        self.method = method

    def __call__(self, *args, **kwargs):
        # do stuff before
        retval = self.method(self, *args, **kwargs)
        # do stuff after
        return retval

class Foobar:
    def __init__(self):
        # initialize stuff
        pass

    @MyDecoratorClass
    def foo(self):
        print(f"foo() called on object {self}")
        return

现在您在这里观察到的是 foo()方法中的 self 被交换了.它不再是 Foobar()实例,而是 MyDecoratorClass()实例:

Now what you observe here is that the self in the foo() method gets swapped. It's no longer a Foobar() instance, but a MyDecoratorClass() instance instead:

>>> foobar = Foobar()
>>> foobar.foo()
foo() called from object <__main__.MyDecoratorClass object at 0x000002DAE0B77A60>

换句话说,方法 foo()失去了其原始身份.这使我们进入了下一个审判.

In other words, the method foo() loses its original identity. That brings us to the next trial.


我尝试保留 foo()方法的原始身份:

I attempt to preserve the original identity of the foo() method:

import functools

class MyDecoratorClass:
    def __init__(self, method):
        functools.update_wrapper(self, method)
        self.method = method

    def __call__(self, *args, **kwargs):
        # do stuff before
        retval = self.method(self.method.__self__, *args, **kwargs)
        # do stuff after
        return retval

class Foobar:
    def __init__(self):
        # initialize stuff
        pass

    @MyDecoratorClass
    def foo(self):
        print(f"foo() called on object {self}")
        return

现在让我们测试一下:

>>> foobar = Foobar()
>>> foobar.foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in __call__
AttributeError: 'function' object has no attribute '__self__'

赞!

编辑
感谢@AlexHall和@ juanpa.arrivillaga为您提供的解决方案.他们俩都工作.但是,它们之间有细微的差别.

EDIT
Thank you @AlexHall and @juanpa.arrivillaga for your solutions. They both work. However, there is a subtle difference between them.

让我们首先看看这个:

def __get__(self, obj, objtype) -> object:
    temp = type(self)(self.method.__get__(obj, objtype))
    print(temp)
    return temp

我引入了一个临时变量,只是为了打印 __ get __()返回的内容.每次访问方法 foo()时,此 __ get __()函数都会返回一个新的 MyDecoratorClass()实例:

I've introduced a temporary variable, just to print what __get__() returns. Each time you access the method foo(), this __get__() function returns a new MyDecoratorClass() instance:

>>> f = Foobar()
>>> func1 = f.foo
>>> func2 = f.foo
>>> print(func1 == func2)
>>> print(func1 is func2)
<__main__.MyDecoratorClass object at 0x000001B7E974D3A0>
<__main__.MyDecoratorClass object at 0x000001B7E96C5520>
False
False

第二种方法(来自@ juanpa.arrivillaga)是不同的:

The second approach (from @juanpa.arrivillaga) is different:

def __get__(self, obj, objtype) -> object:
    temp = types.MethodType(self, obj)
    print(temp)
    return temp

输出:

>>> f = Foobar()
>>> func1 = f.foo
>>> func2 = f.foo
>>> print(func1 == func2)
>>> print(func1 is func2)
<bound method Foobar.foo of <__main__.Foobar object at 0x000002824BBEF4C0>>
<bound method Foobar.foo of <__main__.Foobar object at 0x000002824BBEF4C0>>
True
False

有细微的差别,但是我不确定为什么.

There is a subtle difference, but I'm not sure why.

推荐答案

函数是描述符,因此它们可以自动绑定自身.解决此问题的最简单方法是使用函数实现装饰器,以便为您处理.否则,您需要显式调用描述符.这是一种方法:

Functions are descriptors and that's what allows them to auto-bind self. The easiest way to deal with this is to implement decorators using functions so that this is handled for you. Otherwise you need to explicitly invoke the descriptor. Here's one way:

import functools


class MyDecoratorClass:
    def __init__(self, method):
        functools.update_wrapper(self, method)
        self.method = method

    def __get__(self, instance, owner):
        return type(self)(self.method.__get__(instance, owner))

    def __call__(self, *args, **kwargs):
        # do stuff before
        retval = self.method(*args, **kwargs)
        # do stuff after
        return retval


class Foobar:
    def __init__(self):
        # initialize stuff
        pass

    @MyDecoratorClass
    def foo(self, x, y):
        print(f"{[self, x, y]=}")


@MyDecoratorClass
def bar(spam):
    print(f"{[spam]=}")


Foobar().foo(1, 2)
bar(3)

此处, __ get __ 方法使用绑定方法创建了 MyDecoratorClass 的新实例(以前 self.method 只是一个函数,因为没有实例存在).还要注意, __ call __ 只是调用 self.method(* args,** kwargs)-如果 self.method 现在是绑定方法,则已经隐含了 FooBar self .

Here the __get__ method creates a new instance of MyDecoratorClass with the bound method (previously self.method was just a function since no instance existed yet). Also note that __call__ just calls self.method(*args, **kwargs) - if self.method is now a bound method, the self of FooBar is already implied.

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

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