如何为接收类型注解作为输入并返回对应注解类型值的Python函数添加正确类型标注?
如何为接收类型注解作为输入并返回对应注解类型值的Python函数添加正确类型标注?
我想给一个函数添加类型标注,这个函数接收一个合法的类型注解作为输入参数,返回值的类型和这个注解一致。它不仅要支持type的实例,还要支持像多类型联合或者带标注的类型这类特殊形式。
(底层逻辑是这个函数通过Pydantic把值转换成指定类型。下面的例子里未类型化的值是input_value参数,但实际代码里它是API返回的结果。)
我希望下面的代码能正常工作:
from typing import Annotated, Any, TypeVar, overload from typing_extensions import assert_type from pydantic import TypeAdapter T = TypeVar("T") @overload def convert(value: Any) -> Any: ... @overload def convert(value: Any, output_type: type[T]) -> T: ... def convert(value: Any, output_type: type[T] | Any = Any) -> T: adapter = TypeAdapter(output_type) return adapter.validate_python(value) assert_type(convert(1), Any) assert_type(convert(1, int), int) assert_type(convert(1, int | str), int | str) # "assert_type" mismatch: expected "int | str" but received "Unknown" assert_type(convert(1, Annotated[int, "foo"]), Annotated[int, "foo"]) # Argument of type "Annotated" cannot be assigned to parameter "output_type" of type "type[T@convert]" in function "convert" # Type "Annotated" is not assignable to type "type[T@convert]" # "assert_type" mismatch: expected "int" but received "Unknown"
但目前这个实现只支持output_type是实际的type对象的情况,不支持一般的类型注解。用pyright检查会出现上面代码里标注的内联错误。其他类型检查器的输出如下:
ty check typing_test.py的输出:
error[type-assertion-failure]: Argument does not have asserted type `int | str` --> typing_test.py:24:1 | 22 | assert_type(convert(1, int), int) 23 | 24 | assert_type(convert(1, int | str), int | str) | ^^^^^^^^^^^^---------------------^^^^^^^^^^^^ | | | Inferred type is `Unknown` 25 | # "assert_type" mismatch: expected "int | str" but received "Unknown" | info: `int | str` and `Unknown` are not equivalent types info: rule `type-assertion-failure` is enabled by default error[invalid-argument-type]: Argument to function `convert` is incorrect --> typing_test.py:24:24 | 22 | assert_type(convert(1, int), int) 23 | 24 | assert_type(convert(1, int | str), int | str) | ^^^^^^^^^ Expected `type[Unknown]`, found `<types.UnionType special-form 'int | str'>` 25 | # "assert_type" mismatch: expected "int | str" but received "Unknown" | info: Matching overload defined here --> typing_test.py:13:5 | 12 | @overload 13 | def convert(object: Any, output_type: type[T]) -> T: | ^^^^^^^ -------------------- Parameter declared here 14 | ... | info: Non-matching overloads for function `convert`: info: (object: Any) -> Any info: rule `invalid-argument-type` is enabled by default error[type-assertion-failure]: Argument does not have asserted type `int` --> typing_test.py:27:1 | 25 | # "assert_type" mismatch: expected "int | str" but received "Unknown" 26 | 27 | assert_type(convert(1, Annotated[int, "foo"]), Annotated[int, "foo"]) | ^^^^^^^^^^^^---------------------------------^^^^^^^^^^^^^^^^^^^^^^^^ | | | Inferred type is `Unknown` 28 | # Argument of type "Annotated" cannot be assigned to parameter "output_type" of type "type[T@convert]" in function "convert" 29 | # Type "Annotated" is not assignable to type "type[T@convert]" | info: `int` and `Unknown` are not equivalent types info: rule `type-assertion-failure` is enabled by default error[invalid-argument-type]: Argument to function `convert` is incorrect --> typing_test.py:27:24 | 25 | # "assert_type" mismatch: expected "int | str" but received "Unknown" 26 | 27 | assert_type(convert(1, Annotated[int, "foo"]), Annotated[int, "foo"]) | ^^^^^^^^^^^^^^^^^^^^^ Expected `type[Unknown]`, found `<special-form 'typing.Annotated[int, <metadata>]'>` 28 | # Argument of type "Annotated" cannot be assigned to parameter "output_type" of type "type[T@convert]" in function "convert" 29 | # Type "Annotated" is not assignable to type "type[T@convert]" | info: Matching overload defined here --> typing_test.py:13:5 | 12 | @overload 13 | def convert(object: Any, output_type: type[T]) -> T: | ^^^^^^^ -------------------- Parameter declared here 14 | ... | info: Non-matching overloads for function `convert`: info: (object: Any) -> Any info: rule `invalid-argument-type` is enabled by default Found 4 diagnostics
mypy typing_test.py的输出:
typing_test.py:24: error: Expression is of type "int", not "int | str" [assert-type] assert_type(convert(1, int | str), int | str) ^ typing_test.py:24: error: Argument 2 to "convert" has incompatible type "UnionType | type[int]"; expected "type[int]" [arg-type] assert_type(convert(1, int | str), int | str) ^~~~~~~~~ typing_test.py:27: error: Expression is of type "Any", not "int" [assert-type] assert_type(convert(1, Annotated[int, "foo"]), Annotated[int, "foo"]) ^ typing_test.py:27: error: No overload variant of "convert" matches argument types "int", "<typing special form>" [call-overload] assert_type(convert(1, Annotated[int, "foo"]), Annotated[int, "foo"]) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ typing_test.py:27: note: Possible overload variants: typing_test.py:27: note: def convert(object: Any) -> Any typing_test.py:27: note: def [T] convert(object: Any, output_type: type[T]) -> T Found 4 errors in 1 file (checked 1 source file)
我想给convert函数添加正确的类型标注,同时不改变它的逻辑行为。
这个问题并不是重复问题——之前有个类似问题的解决方案只支持具体的type类型,不支持一般的类型注解。我是用那个方案作为起点的,但现在需要一个能支持所有类型注解的解决方案,类似Pydantic本身或者typing.cast()函数的行为。
可能问题的根源在于我写的output_type: type[T]标注是错误的,我不知道该如何给“类型注解”这个概念添加类型标注。
备注:内容来源于stack exchange,提问作者palotasb
相关产品推荐
相关产品推荐

