目前我正在使用
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
通过装饰器来实现。
我知道我可以
assert
isinstance
或类似的东西,但自动隐式方法会更好。
作为对用输入信息包装方法的建议的回应,我将分享我的实际用例。
我正在使用
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
,它的形状会有所不同。它还需要简短,以便我可以将其用作文档并捕获真正发生的错误。
“在我想要调用的函数周围不使用装饰器的原因是因为库函数通常没有类型提示”
然后只需向它们添加类型提示即可。
至少有两种方法可以做到这一点。
第一个选项是,创建一个新函数,其唯一原因是向其添加所需的类型提示,例如:
>>> 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')
>>>
__annotations__
特殊属性然后装饰它
(这不适用于内置函数和/或 c 扩展,仅适用于那些使用纯 python 创建的函数,因此您不能对 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。带注释的赋值语句
如果一个名字被注释在函数作用域中,那么这个名字就是局部的 对于该范围。注释从不被评估并存储在函数中 范围。
由于这些信息在运行时丢失,任何程度的黑客装饰器都无法知道函数内的给定变量以某种方式进行了注释,因此它只是静态类型检查器的一个工件,可以直接查看源代码(例如您的IDE),所以如果您希望在运行时添加这些类型检查,您将需要一个编译器在代码中添加这些类型检查...如果您想要这样,那么您可能需要考虑是否最好使用不同的相反,提供本机类型检查和强制机制的语言。
除此之外,使用我建议的解决方案之一或默认为
assert
,isinstance
等......看起来是唯一的方法
pip install runtime-type-checker
您可以通过 check_type 函数根据类型或注释检查对象。
如果检查成功,该函数将返回 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