簡體   English   中英

Python 裝飾器的類型注釋

[英]Python Type annotations for decorators

我有一個裝飾器,它檢查實例屬性self.enabled並在未啟用時返回 0。 否則,它返回方法的返回值,它是一個 int,即傳入列表中唯一字符串的索引。

def check_if_enabled(func: Callable[..., int]) -> Callable[..., int]:
    @wraps(func)
    def wrapper(cls, list_of_strings):
        if not cls.enabled:
            return 0
        return func(cls, list_of_strings)
    return wrapper

我想讓類型注釋更具體,但我不知道該怎么做。

Callable[..., int]顯然是我想要改變的,我想讓它這樣Callable需要兩個 arguments、一個 class 的實例和一個字符串列表。 這可能嗎?

首先,請記住盡可能提供完整的工作示例。 用更多的代碼和更少的文字記錄你的問題可以更容易地理解你想要實現的目標,並且我們不太可能誤解。

我假設你想裝飾一個類似以下的方法:

class Example(object):
    @check_if_enabled
    def func(self,  # implicit Example
             list_of_strings: List[str]
             ) -> int:
        pass

首先要做的是確定它的Callable的外觀。 您將 function 參數列表作為第一個參數傳遞,返回值作為第二個參數傳遞。 請注意,第一個參數是self ,即 class 實例。 因此,它要么是:

Callable[[Example, List[str]], int]

或者如果您不想將其限制為一個特定的 class:

Callable[[object, List[str]], int]

請注意,您可能希望在聲明Example之前使用此原型。 為了允許這樣做,您需要傳遞"Example" ,即 class 名稱作為字符串。 這被視為 class 的前向聲明。


所以你的代碼注釋將是:

def check_if_enabled(func: Callable[["Example", List[str]], int]
                     ) -> Callable[["Example", List[str]], int]:
    @wraps(func)
    def wrapper(cls: "Example",
                list_of_strings: List[str]
                ) -> int:
        if not cls.enabled:
            return 0
        return func(cls, list_of_strings)
    return wrapper

或作為一個完整的工作示例:

from functools import wraps
from typing import Callable, List


def check_if_enabled(func: Callable[["Example", List[str]], int]
                     ) -> Callable[["Example", List[str]], int]:
    @wraps(func)
    def wrapper(cls: "Example",
                list_of_strings: List[str]
                ) -> int:
        if not cls.enabled:
            return 0
        return func(cls, list_of_strings)
    return wrapper


class Example(object):
    def __init__(self, enabled: bool) -> None:
        self.enabled = enabled

    @check_if_enabled
    def func(self,  # implicit Example
             list_of_strings: List[str]
             ) -> int:
        print('yes, it is')
        return 10


ex = Example(True)
print(ex.func(['1', '2', '3']))

ex = Example(False)
print(ex.func(['1', '2', '3']))

產生:

yes, it is
10
0

作為一般說明,您可能希望您的裝飾器是通用的,而不是只適合一種特定的方法。 例如,上面的裝飾器可以通過使用*args, **kwargs和寬松的類型來擴展以適應任何返回int的方法:

def check_if_enabled(func: Callable[..., int]
                     ) -> Callable[..., int]:
    @wraps(func)
    def wrapper(cls: "Example",
                *args: Any,
                **kwargs: Any
                ) -> int:
        if not cls.enabled:
            return 0
        return func(cls, *args, **kwargs)
    return wrapper

更進一步,您可以允許通用返回值並將其作為參數傳遞給裝飾器:

def check_if_enabled(return_if_disabled: Any
                     ) -> Callable[[Callable[..., int]],
                                   Callable[..., int]]:
    def decorator(func: Callable[..., int]
                  ) -> Callable[..., int]:
        @wraps(func)
        def wrapper(cls: "Example",
                    *args: Any,
                    **kwargs: Any
                    ) -> Any:
            if not cls.enabled:
                return return_if_disabled
            return func(cls, *args, **kwargs)
        return wrapper
    return decorator


class Example(object):
    def __init__(self, enabled: bool) -> None:
        self.enabled = enabled

    @check_if_enabled(return_if_disabled=0)
    def func(self,  # implicit Example
             list_of_strings: List[str]
             ) -> int:
        print('yes, it is')
        return -1

雖然這當然會完全殺死 static 打字。


如果有疑問,我建議使用mypy 如果您弄錯了,它可以建議正確的類型。

最后,我不認為@wraps在這里做任何事情。 我保留了它,因為它存在於粘貼的代碼中。

希望這可以幫助。

暫無
暫無

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

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