簡體   English   中英

使用裝飾器在python中的類方法上設置類的屬性

[英]Set the property of a class using decorators on a class method in python

我有一個ABC類,里面有一個功能someFunction

我不想弄亂someFunction的代碼,因此用@MyDecorator包裝它。 當我調用someFunction時,該MyDecorator如何修改ABC類的屬性?

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

你可以試試:

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現在是一個描述符,因為它實現了__get__

通過某個類或該類的實例訪問MyDecorator的任何實例的屬性時,將調用__get__

__get__將在參數instance接收該所有者對象的instance ,從那里我們可以返回一個新的instancemethod instancemethod ,該instance隱式接收該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的新類,否則將不起作用。

您需要在文件中定義MyDecorator的位置早於使用它的位置,否則會收到“ NameError:未定義名稱'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

ld更新

print "some property is", abc.some_property

我運行了它,它起作用了; 它打印

Hello world
some property is Modified value

因此,這可以驗證它確實修改了abc中的some_property。 它以args[0].some_property的形式訪問,因為調用修飾的函數(作為對象方法)時, self作為第一個參數傳遞。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM