实例方法的装饰器

时间:2018-07-26 15:05:27

标签: python python-3.x python-decorators

在“样板”中包装类的方法Python装饰器将把该方法视为常规函数,并使其失去引用类实例对象的__self__属性。可以避免吗?

参加以下课程:

class MyClass(object):
    def __init__(self, a=1, b=2):
        self.a = a
        self.b = b
    def meth(self):
        pass

如果未修饰meth(),则MyClass().meth.__self__引用实例方法并启用类似setattr(my_class_object.meth.__self__, 'a', 5)的内容。

但是在将任何东西包装在装饰器中时,只会传递函数对象;实际绑定到的对象不会随其传递。 (请参见this答案。)

import functools

def decorate(method):
    @functools.wraps(method)
    def wrapper(*args, **kwargs):
        # Do something to method.__self__ such as setattr
        print(hasattr(method, '__self__'))
        result = method(*args, **kwargs)
        return result
    return wrapper

class MyClass(object):
    def __init__(self, a=1, b=2):
        self.a = a
        self.b = b
    @decorate
    def meth(self):
        pass

MyClass().meth()
# False            <--------

可以覆盖吗?

3 个答案:

答案 0 :(得分:3)

您在这里的主要误解是操作顺序。

当调用decorate()装饰器时,meth()还不是一种方法-它仍然是一个函数-仅当class块位于meth上方时由元类描述符转换为方法! -这就是为什么它还没有__self__的原因。

换句话说,要装饰方法,您必须忽略它们是方法并将其视为常规函数的事实-因为调用装饰器时就是这样。

实际上,原始的meth函数永远不会变成一种方法-从装饰器返回的函数wrapper将成为类的一部分,并且将成为获得{ {1}}属性。

答案 1 :(得分:2)

让我澄清一下装饰过程:

在类meth中用decorate装饰MyClass时,您正在做的事情:

class MyClass(object):
    ... omit
    meth = decorate(meth)  # the meth in "()" is the original function.

如您所见,decoratemethod作为函数,并返回wrapper,这是另一个功能。现在,meth中的原始MyClass被新的wrapper所代替。因此,当您调用myclass_instance.meth()时,就是在调用新的wrapper函数。

没有任何黑魔法,因此self可以肯定地传递到wrapper中,并且可以安全地使用self接受wrapper(self, *args, **kwargs)

答案 2 :(得分:1)

如果装饰类的方法,则第一个参数始终是self对象(可以使用args[0]访问它):

import functools

def decorate(method):
    @functools.wraps(method)
    def wrapper(*args, **kwargs):
        print(hasattr(args[0], 'a'))
        result = method(*args, **kwargs)
        return result
    return wrapper

class MyClass(object):
    def __init__(self, a=1, b=2):
        self.a = a
        self.b = b
    @decorate
    def meth(self):
        pass

MyClass().meth()

打印:

True

编辑:

您还可以在self函数中根据具体评论指定wrapper

import functools

def decorate(method):
    @functools.wraps(method)
    def wrapper(self, *args, **kwargs):
        print(hasattr(self, 'a'))
        result = method(self, *args, **kwargs)
        return result
    return wrapper

class MyClass(object):
    def __init__(self, a=1, b=2):
        self.a = a
        self.b = b
    @decorate
    def meth(self):
        pass

MyClass().meth()

也打印:

True
相关问题