最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - Typing recursive type annotation - Stack Overflow

programmeradmin7浏览0评论

I have a function which takes a value, a type annotation, and validates that the value is of the type.

It only takes a certain subset of type annotations: int, float, str, bool, a list of any of these, or a (potentially nested) list of any of these.

def validate(value: Any, type_: ValidatableAnnotation):
    ...

I have no problem implementing the function, however, I have a problem typing it.

I'm trying to define the type ValidatableAnnotation to allow any level of recursion in the list type e.g. list[int], list[list[int]] etc. ad infinitum.

My best attempt:

ValidatableAnnotation = (
    Type[str] | Type[int] | Type[float] | Type[bool] | Type[list["ValidatableAnnotation"]]
)

However, while it works for the atomic values, it doesn't work for lists:

validate([1], list[int])  # Incompatible types in assignment (expression has type "type[list[int]]", variable has type "ValidatableAnnotation")

Is the Python (3.10) type system capable of doing what I want here, and if so how do I do it?

Edit: similarity has been pointed out to What's the correct type hint for a type hint?. However, it is possible that an answer is available at a lower level of generality than at a higher level. I'm not asking for a general answer to "how to define a type hint for a type hint", but for a specific answer to "how to recursively define a type hint for a list type hint. This specific scenario may have a solution even if the more general question doesn't.

I have a function which takes a value, a type annotation, and validates that the value is of the type.

It only takes a certain subset of type annotations: int, float, str, bool, a list of any of these, or a (potentially nested) list of any of these.

def validate(value: Any, type_: ValidatableAnnotation):
    ...

I have no problem implementing the function, however, I have a problem typing it.

I'm trying to define the type ValidatableAnnotation to allow any level of recursion in the list type e.g. list[int], list[list[int]] etc. ad infinitum.

My best attempt:

ValidatableAnnotation = (
    Type[str] | Type[int] | Type[float] | Type[bool] | Type[list["ValidatableAnnotation"]]
)

However, while it works for the atomic values, it doesn't work for lists:

validate([1], list[int])  # Incompatible types in assignment (expression has type "type[list[int]]", variable has type "ValidatableAnnotation")

Is the Python (3.10) type system capable of doing what I want here, and if so how do I do it?

Edit: similarity has been pointed out to What's the correct type hint for a type hint?. However, it is possible that an answer is available at a lower level of generality than at a higher level. I'm not asking for a general answer to "how to define a type hint for a type hint", but for a specific answer to "how to recursively define a type hint for a list type hint. This specific scenario may have a solution even if the more general question doesn't.

Share Improve this question edited Mar 27 at 10:49 samfrances asked Mar 27 at 8:12 samfrancessamfrances 3,7673 gold badges28 silver badges48 bronze badges 8
  • This question is similar to: Type hint for an object that can be used as a type hint itself. If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem. – InSync Commented Mar 27 at 8:40
  • 1 I assume instead of Type[list["ValidatableAnnotation"]] you should just have list["ValidatableAnnotation"] because it should be the list of types, not the type of the list of types. Also small side node, just use type instead of Type, because it is easier and the old typing aliases were deprecated in version 3.9. docs.python./3/library/typing.html#typing.Type – JoniKauf Commented Mar 27 at 8:49
  • @JoniKauf That enables x: ValidatableAnnotation = [int, float]. I want to enable x: ValidatableAnnotation = list[int], x: ValidatableAnnotation = list[float] x: ValidatableAnnotation = list[list[int]] etc. – samfrances Commented Mar 27 at 9:22
  • 1 No, you can't do this, because lists are invariant; a list of union types is not equivalent to a union of list types. As you've noted, the other types int str float bool work. Also, best not to use an alias comprising type[...]s; you should alias str | int | ... then do type_: type[ValidatableAnnotation] instead. Try this. – dROOOze Commented Mar 27 at 11:00
  • 1 @dROOOze Your comment is what worked best for me. Put it as an answer and I'll accept. If you feel like putting more explanation into the invariant issue, that would be appreciated. – samfrances Commented Mar 27 at 15:44
 |  Show 3 more comments

1 Answer 1

Reset to default 1

What you need is a TypeForm from https://peps.python./pep-0747/. Which has now been released with typing_extensions 4.13.

If want to strictly insist on a list, the code at the bottom will.

Alternatively go for if any sequence is acceptable:

type ValidatableAnnotation = (
    str | int | float | bool | Sequence["ValidatableAnnotation"]
)

Second note, that you have to differentiate between a TypeForm[ValidatableAnnotation] which can just be a string representing a type and a ValidatableAnnotation which will be a valid object.

Code sample in pyright playground

from typing_extensions import TypeForm, Type, Any, Sequence, TypeVar

# Define valid types here or in a variable/typea alias
type ValidatableAnnotation[typeT: str | int | float | bool] = (
    typeT | list["ValidatableAnnotation[typeT]"]
)

def validate(value: Any, type_: TypeForm[ValidatableAnnotation]):
    ...

validate([1], list[int]) 

# Not a Type Expression, should be rejected
x_form: TypeForm[ValidatableAnnotation] = [int, float]
# Okay as a value
x_value: ValidatableAnnotation = [int, float]

y: TypeForm[ValidatableAnnotation] = list[int]

z: TypeForm[ValidatableAnnotation] = list[float]
u: TypeForm[ValidatableAnnotation] = list[list[int]]
发布评论

评论列表(0)

  1. 暂无评论