Python中的类型检查内联类型提示

2024-04-27 14:07:32 发布

您现在位置:Python中文网/ 问答频道 /正文

目前,我正在使用typeguard.typechecked来修饰我的函数,并检查重要函数的输入和输出

from typeguard import typechecked
@typechecked  # Gives me an error if argument/return types don't match the hints
def takes_int_makes_float(x: int) -> float:
    return float(x)

但是,如果我想检查来自我无法控制的函数的类型,以及当类型不是我想要的类型时出错,该怎么办

@proposed_decorator
def does_something_interesting():
    # Implicitly assert that this is the right type.
    # Note that this is valid Python code already, and works as a hint to my IDE.
    x: float = makes_float_or_tuple()
    print(x)

有没有办法做到这一点?当然,可以使用ast实现装饰器

我知道我可以{}{}或类似的方法,但是自动隐式方法会更好

编辑:

作为对使用键入信息包装方法的建议的回应,我将分享我的实际用例

我正在使用torchtyping来记录并确保PyTorch张量的形状

from torchtyping import TensorType
import torch

def interesting_reshaping_method(x: TensorType['batch', 'num_points', 'point_dim']):
    lengths: TensorType['batch', 'num_points', 1] = torch.norm(x, dim=2, keepdim=True)
    # ... do something that requires this exact shape to do what I want,
    # but will fail silently if the shape isn't what I want.

在这种情况下,我需要显式地检查张量的类型,如果我使用了keepdim=False或一些不同的dim,它的形状就会不同。它还需要简短,以便我可以使用它作为文档,以及捕捉真正发生的bug


Tags: the方法函数fromimport类型ifthat
2条回答

安装:

pip install runtime-type-checker

描述

可以通过“检查类型”功能根据类型或注释检查对象

如果检查成功,该函数将返回None,如果出现错误,则会引发TypeError

请注意,此函数不会递归检查类的属性等

from typing import List, Sequence, Optional, Mapping
from dataclasses import dataclass
from runtime_type_checker import check_type


check_type("a", str)  # OK
check_type(["a"], List[str])  # OK
check_type(["a", 1], Sequence[str])  # raises TypeError


@dataclass
class Foo:
    a: int
    b: Optional[Mapping[str, int]] = None


check_type(Foo(1), Foo)  # OK
check_type(Foo(1), int)  # raises TypeError

链接:https://pypi.org/project/runtime-type-checker/

"The reason for not using a decorator around the function I want to call is because library functions often don't have type hints"

然后只需向它们添加类型提示

至少有两种方法可以做到这一点

第一个选项是,创建新函数的唯一原因是向其添加所需的类型提示,例如:

>>> from typeguard import typechecked
>>> from typing import Optional, Tuple, get_type_hints
>>> import mimetypes #our guinea pig for the example :p
>>> get_type_hints(mimetypes.guess_type)
{}
>>> @typechecked
def guess_type_bad(*a,**k) -> Tuple[str,Optional[int]]: #deliberately bad to see that it works
    return mimetypes.guess_type(*a,**k)

>>> guess_type_bad("test.txt")
('text/plain', None)
>>> guess_type_bad("test.tar.gz")
Traceback (most recent call last):
  File "<pyshell#38>", line 1, in <module>
    guess_type_bad("test.tar.gz")
  File "C:\Python39\lib\site-packages\typeguard\__init__.py", line 1019, in wrapper
    raise TypeError(*exc.args) from None
TypeError: type of the return value[1] must be one of (int, NoneType); got str instead
>>>
>>> @typechecked
def guess_type_good(*a,**k) -> Tuple[str,Optional[str]]:
    return mimetypes.guess_type(*a,**k)

>>> guess_type_good("test.tar.gz")
('application/x-tar', 'gzip')
>>>

第二个选项是直接写入它们的^{}特殊属性,然后修饰它

(这不适用于内置函数和/或c扩展,仅适用于使用纯python创建的函数和/或c扩展,因此您不能对math.cos或numpy.array这样做)

>>> mimetypes.guess_type.__annotations__["return"]=Tuple[str,Optional[str]]
>>> guess_type = typechecked(mimetypes.guess_type)  #decoration with "@" is just syntactic sugar for this
>>> guess_type("test.tar.gz")
('application/x-tar', 'gzip')
>>> help(guess_type)
Help on function guess_type in module mimetypes:

guess_type(url, strict=True) -> Tuple[str, Optional[str]]
    Guess the type of a file based on its URL.
    
    [....]

>>>     

对于某些内联版本,似乎很简单,不可能为函数动态执行此操作: 7.2.2. Annotated assignment statements

If a name is annotated in a function scope, then this name is local for that scope. Annotations are never evaluated and stored in function scopes.

由于这些信息在运行时丢失,没有任何恶意的装饰程序可以知道函数中的给定变量是以某种方式注释的,因此这只是可以直接查看源代码(例如IDE)的静态类型检查器的工件,因此,如果您希望在运行时将这些类型检查添加到代码中,则需要一个编译器。。。如果你想这样做,那么你可能需要考虑是否更好地使用一种不同的语言来提供一种本地类型的检查和执行机制。p>

除此之外,使用我建议的解决方案之一,或者默认为assertisinstance,等等。。。那看起来是唯一的办法

相关问题 更多 >