在python中的类方法上使用装饰器设置类的属性

时间:2014-06-14 20:10:14

标签: python python-decorators

我有一个班级ABC,里面有一个函数someFunction

我不想搞砸someFunction的代码,因此我用@MyDecorator包装它。当我致电MyDecorator时,ABC如何修改班级someFunction的属性?

class ABC(object):
    def __init__(self):
        self.someProperty = "Initial value"

    @MyDecorator
    def someFunction(self):
        print "Hello world"


class MyDecorator(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        # ...??? 
        return self.func(*args, **kwargs)

abc = ABC()
abc.someFunction()  #   When calling someFunction(), abc.someProperty would be updated

2 个答案:

答案 0 :(得分:1)

您可以尝试:

class MyDecorator(object):
    def __init__(self, func):
        self.func = func
    def __call__(self, instance, *args, **kwargs):
        instance.someProperty = "New value"
        return self.func(instance, *args, **kwargs)
    def __get__(self, instance, owner):
        if instance is None:
            return self
        return self.__call__.__class__(self, instance)

class ABC(object):
    def __init__(self):
        self.someProperty = "Initial value"
    @MyDecorator
    def someFunction(self):
        print("Hello world")

MyDecorator现在是descriptor,因为它实现了__get__

当通过某个类或该类的实例访问MyDecorator的任何实例的属性时,将调用__get__

__get__将在参数instance中收到该所有者对象的实例,从那里我们可以返回instancemethod的新实例,该实例将隐式接收instance

>>> abc.someFunction.__class__
<type 'instancemethod'>
>>> abc.someFunction.__class__.__doc__
'instancemethod(function, instance, class)\n\nCreate an instance method object.'
>>>

它需要function,基本上任何可调用对象都可以使用,因为MyDecorator是类,__call__会修改自己的self

行动中:

>>> abc = ABC()
>>> abc.someProperty
'Initial value'
>>> abc.someFunction()
Hello world
>>> abc.someProperty
'New value'
>>>

请注意,在Python 2.X中,MyDecorator必须是继承object或无法工作的新式类。

答案 1 :(得分:0)

您需要在文件中先前定义MyDecorator,而不是在使用它时,或者您得到“NameError:name'MyDecorator'未定义”。

当我说“装饰者应该返回一个函数”时,我错了。这适用于用作装饰器的函数,如我的示例所示。但是,当使用类时,它不必返回函数。它返回该类中的对象。如果“类MyDecorator”被命名为“类MyModifiedFunction”而不是混淆,因为类的一个元素被用作装饰函数。

通常,装饰器返回的函数将调用原始函数。你的没有。如果您想完全替换someFunction而不是仅修改它,则不必如此。

您不需要将MyDecorator设为类。你可以把它变成一个功能。这是一个例子:

def MyDecorator(original_function):
    def replacement_function(*args, **kwargs):
        args[0].some_property = some_new_value
        return(original_function(*args, **kwargs))
    return(replacement_function)

按照代码的前半部分进行操作,您可以在其中定义类ABC

请注意,当它显示return(original_function(*args, **kwargs))时,它正在运行原始函数并返回原始函数返回的任何内容。如果原始函数返回一个对象,它将返回该对象。然而,当它说return(replacement_function)它正在做一些完全不同的事情时:它当时没有运行替换功能;它只是将函数作为返回值返回。装饰者必须返回一个函数。

(@ dano:好的。我认为我修好了。)

以上是更完整形式的上述建议:

def MyDecorator(original_function):
    def replacement_function(*args, **kwargs):
        args[0].some_property = 'Modified value'
        return(original_function(*args, **kwargs))
    return(replacement_function)

class ABC(object):
    def __init__(self):
        self.some_property = "Initial value"

    @MyDecorator
    def some_function(self):
        print "Hello world"

abc = ABC()
abc.some_function()  #   When calling some_function(), abc.some_property wou

要更新

print "some property is", abc.some_property

我跑了这个并且它有效;它打印

Hello world
some property is Modified value

所以这验证它确实修改了abc中的some_property。它作为args[0].some_property访问,因为当调用修饰函数(作为对象方法)时,self作为第一个参数传入。