这个装饰器类型是否正确注释? [关闭]

问题描述 投票:-3回答:2
def timer(func: Callable[..., Any]) -> Callable[..., Any]:
"""Calculates the runtime of a function, and outputs it to logging.DEBUG."""

@wraps(func)
def wrapper(*args, **kwargs):
    start = perf_counter()
    value = func(*args, **kwargs)
    end = perf_counter()
    _logger = logging.getLogger(__name__ + '.' + func.__name__)
    _logger.debug(' runtime: {:.4f} seconds'.format(end - start))
    return value

return wrapper
python python-3.x mypy
2个回答
1
投票

缩进似乎有点偏离这里,但是否则,是的,类型不是不正确的。但是,你可以使这更加精确。您输出的函数与作为输入的函数具有相同的返回类型,但您没有注意到这一点。

特别是,你可以这样说

from typing import Callable, TypeVar

T = TypeVar("T")
def timer(func: Callable[..., T]) -> Callable[..., T]:

看起来你应该能够对args / kwargs做同样的事情,但我在自己的打字体验中并没有遇到过这种情况,所以我不能确切地说出如何做。编辑 - 有关打字的更多信息,请参阅this GitHub issue;它似乎不可能(但是?)。

我想你也可以说

def timer(func: T) -> T:

但这似乎并没有用。


3
投票

这种方法的问题是,现在,MyPy失去了返回类型,或者更确切地说,它退化为Any,因此请考虑:

import logging
from typing import Callable, Any
from time import perf_counter
from functools import wraps

def timer(func: Callable[..., Any]) -> Callable[..., Any]:
    """Calculates the runtime of a function, and outputs it to logging.DEBUG."""

    @wraps(func)
    def wrapper(*args, **kwargs):
        start = perf_counter()
        value = func(*args, **kwargs)
        end = perf_counter()
        _logger = logging.getLogger(__name__ + '.' + func.__name__)
        _logger.debug(' runtime: {:.4f} seconds'.format(end - start))
        return value
    return wrapper

@timer
def func(x:int) -> int:
    return x


def string_func(s: str):
    return s[:]

x = 42 * func(42)

reveal_type(x)

string_func(x)

使用:

(py37) Juans-MacBook-Pro:tempdata juan$ mypy --version
mypy 0.641

如果我尝试打字检查,它会通过!

(py37) Juans-MacBook-Pro:tempdata juan$ mypy typing_decorators.py
typing_decorators.py:29: error: Revealed type is 'Any'

我找到了一个解决方案,在这种情况下,你想要准确保存参数,即使用TypeVarcast包装器,这样MyPy可以确切地知道类型(假设原始函数已经消息):

import logging
from typing import Callable, Any, TypeVar, cast
from time import perf_counter
from functools import wraps


F = TypeVar('F', bound=Callable[..., Any])

def timer(func: F) -> F:
    """Calculates the runtime of a function, and outputs it to logging.DEBUG."""

    @wraps(func)
    def wrapper(*args, **kwargs):
        start = perf_counter()
        value = func(*args, **kwargs)
        end = perf_counter()
        _logger = logging.getLogger(__name__ + '.' + func.__name__)
        _logger.debug(' runtime: {:.4f} seconds'.format(end - start))
        return value
    return cast(F, wrapper)

@timer
def func(x:int) -> int:
    return x


def string_func(s: str):
    return s[:]

x = 42 * func(42)

reveal_type(x)

string_func(x)

在这种情况下:

(py37) Juans-MacBook-Pro:tempdata juan$ mypy typing_decorators.py
typing_decorators.py:32: error: Revealed type is 'builtins.int'
typing_decorators.py:34: error: Argument 1 to "string_func" has incompatible type "int"; expected "str"
© www.soinside.com 2019 - 2024. All rights reserved.