请考虑以下代码:

from functools import wraps
from netmiko import ConnectHandler

class my_class():

    def __init__(self):
        self.connection = ConnectHandler()

    def validate_connection():
        @wraps(func)
        def inner(self, *args, **kwargs):
            if self.connection is None:
                raise Exception(
                    f"No connection to {self.name}({self.ip}):22\n"
                )
            return func(self, *args, **kwargs)

        return inner


    @validate_connection
    def send_command(self, *args, **kwargs) -> Union[str, list[Any], dict[str, Any]]:
        return self.connection.send_command(*args, **kwargs, read_timeout=60)

正确的打字方式是什么?提示validate_connection()方法,并请解释你是如何计算出来的.

目前,MyPy在第@validate_connection行给出了以下错误

Argument 1 to "validate_connection" has incompatible type "Callable[[my_class, VarArg(Any), KwArg(Any)], str | list[Any] | dict[str, Any]]"; expected "my_class"

推荐答案

我会把decorator 拉到一个顶级函数中,然后添加一个显式的typehint到self.然后,您可以为函数参数创建一个typvar,这将确保类上的结果方法保持其原始签名.

这个示例中有一个强制转换,它确实假设它将始终用于特定的类(假设self是"MyClass"),但它确实极大地简化了类型提示.

from functools import wraps
from typing import Any, TypeVar, Callable, cast, List

Func = TypeVar("Func", bound=Callable[..., Any])


def validate_connection(func: Func) -> Func:
    @wraps(func)
    def inner(self: "MyClass", *args: Any, **kwargs: Any) -> Any:
        if self.connection is None:
            raise Exception
        return func(self, *args, **kwargs)

    return cast(Func, inner)


class MyClass:
    def __init__(self) -> None:
        self.connection = None

    @validate_connection
    def send_command(self, foo: int, bar: str) -> List[str]:
        return []

使用mypy输出验证的示例:

MyClass().send_command(1, "2").append("3")
# No error :)

MyClass().send_command(1, "2").append(3)
# error: Argument 1 to "append" of "list" has incompatible type "int"; expected "str"  [arg-type]

MyClass().send_command(1, 2).append("3")
# error: Argument 2 to "send_command" of "MyClass" has incompatible type "int"; expected "str"  [arg-type]

请注意,您也可以 Select 将此设置为@staticmethod,其余所有设置仍然适用

class MyClass:
    def __init__(self) -> None:
        self.connection = None

    @staticmethod
    def validate_connection(func: Func) -> Func:
        @wraps(func)
        def inner(self: "MyClass", *args: Any, **kwargs: Any) -> Any:
            if self.connection is None:
                raise Exception
            return func(self, *args, **kwargs)

        return cast(Func, inner)

    @validate_connection
    def send_command(self, foo: int, bar: str) -> List[str]:
        return []

Python相关问答推荐

如何在BeautifulSoup中链接Find()方法并处理无?

将特定列信息移动到当前行下的新行

Django mysql图标不适用于小 case

使用@ guardlasses. guardlass和注释的Python继承

删除字符串中第一次出现单词后的所有内容

如何在python polars中停止otherate(),当使用when()表达式时?

有没有一种方法可以从python的pussompy比较结果中提取文本?

如何在图中标记平均点?

干燥化与列姆化的比较

如何将数据帧中的timedelta转换为datetime

pandas fill和bfill基于另一列中的条件

如何在FastAPI中替换Pydantic的constr,以便在BaseModel之外使用?'

根据Pandas中带条件的两个列的值创建新列

如何在Airflow执行日期中保留日期并将时间转换为00:00

Django.core.exceptions.SynchronousOnlyOperation您不能从异步上下文中调用它-请使用线程或SYNC_TO_ASYNC

用0填充没有覆盖范围的垃圾箱

如何在Python中实现高效地支持字典和堆操作的缓存?

有没有一种方法可以根据不同索引集的数组从2D数组的对称子矩阵高效地构造3D数组?

Python:在cmd中添加参数时的语法

为什么这个正则表达式没有捕获最后一次输入?