簡體   English   中英

用裝飾器類裝飾方法

[英]Decorate method with decorator class

我正在嘗試為方法創建包裝。 裝飾器本身被實現為覆蓋__call__方法的類。

這適用於修飾函數,但是如果以這種方式修飾方法,則self不會綁定到修飾的函數。

以下是一個最小的示例:

import functools


class Wrapper:

    def __init__(self, func) -> None:
        super().__init__()
        self.__func = func
        functools.update_wrapper(self, func)

    def __call__(self, *args, **kwargs):
        print("PreBar")
        result = self.__func(*args, **kwargs)
        print("PostBar")
        return result


def MyDecorator(func):
    return Wrapper(func)


class Foo:
    baz = "Baz"

    @MyDecorator
    def bar(self):
        print(self.baz)
        return self.baz


if __name__ == '__main__':
    foo = Foo()
    print(foo.bar)
    print(f"Result of foo.bar(): {foo.bar()}")

    print(Foo.bar)
    print(f"Result of Foo.bar(foo): {Foo.bar(foo)}")

這將產生以下錯誤,這意味着self不會像普通方法那樣自動傳遞給__call__調用:

Traceback (most recent call last):
  File "/mypath/test.py", line 35, in <module>
    print(f"Result of foo.bar(): {foo.bar()}")
  File "/mypath/test.py", line 14, in __call__
    result = self.__func(*args, **kwargs)
TypeError: bar() missing 1 required positional argument: 'self'

問題:如何將返回的可調用包裝器對象綁定到self 將對象包裝在函數中以讓python進行綁定是一種選擇,但是在我的情況下不是這樣,因為需要實際的對象。

問題如何用裝飾器類裝飾實例方法? 類似於我要實現的目標。 如果僅需要綁定,則此處提供的解決方案有效。 我需要的是,訪問屬性時返回的對象實例是Wrapper類型,而不是functools.partial類型。

第二個問題的答案也不起作用,因為通過返回的實例__get__是類型的method ,而不是Wrapper

以下文章通過簡單地將對象包裝在函數中來解決該問題,以便python可以將self與本機機制綁定: 裝飾方法(類方法重載)

正如問題中提到的那樣,這不是我的選擇。 該帖子還提到了替代方法:為Wrapper類實現描述符協議。 裝飾方法的問題也解決了這個問題。 那里的問題是,那里使用的方法僅適用於該類的一個實例。

我想出了以下解決方案:

import functools


class Wrapper:

    def __init__(self, func, instance=None) -> None:
        super().__init__()
        self.__func = func
        self.__instance = instance
        self.__bindings = {}
        functools.update_wrapper(self, func)

    def __call__(self, *args, **kwargs):
        print("PreBar")
        if self.__instance is not None:
            args =(self.__instance, ) + args
        result = self.__func(*args, **kwargs)
        print("PostBar")
        return result

    def __get__(self, instance, owner):
        try:
            return self.__bindings[(instance, owner)]
        except KeyError:
            binding = self.__bindings[(instance, owner)] = Wrapper(self.__func, instance)
            return binding


def MyDecorator(func):
    return Wrapper(func)


class Foo:
    baz = "Baz"

    @MyDecorator
    def bar(self):
        print(self.baz)
        return self.baz


if __name__ == '__main__':
    foo = Foo()
    print(foo.bar)
    print(f"Result of foo.bar(): {foo.bar()}")

    print(Foo.bar)
    print(f"Result of Foo.bar(foo): {Foo.bar(foo)}")

區別在於,函數描述符的__get__方法為每個(實例,所有者)對創建一個新的Wrapper實例。 通過這種方式,包裝程序始終始終僅綁定到一個實例,甚至是類本身。

暫無
暫無

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

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