我想使用泛型,这样我就可以避免使用"不变的Xyz"问题,并为几个大型类型结构实现提供单一的事实来源,而不必联合泛型类的内部和外部。我的代码为自定义数据结构提供了一系列嵌套的别名,这基本上是一个由字典和列表组成的大JSON,其中"叶子"是"叶子"。是两种可能类型中的一种(称为A
和B
),并且只有"A
独占",或"B
exclusively"对于给定的树。然而,整个数据模型的一些子结构(如List[A]
或List[B]
)可能发生在字典树的不同层中,当它们的逻辑完全相同时,我不想为每一层维护特定的类型。
我将使用不变式List
作为示例。我想有一个别名C = List[A | B]
识别List[A]
和List[B]
作为有效的子类型,而不必在任何地方做C = List[A] | List[B]
,因为工会需要歧视才能在我的效用函数中正常工作。我希望A
和B
被GenericAorB
所取代,如果我有List[A]
或List[B]
在我的实用程序中自动推断。
我从这里得到了灵感,因为这似乎是我所需要的:https://mypy.readthedocs.io/en/stable/generics.html type-variables-with-value-restriction
然而,它似乎不工作。
from typing import TypeVar, List
GenericStringOrFloat = TypeVar("GenericStringOrFloat", str, float)
ListStringOrFloat = List[GenericStringOrFloat]
my_list : ListStringOrFloat
my_list = [12.0, 13.0] # Fine
my_list = ["hello", "world"] # Fine
my_list = ["hello", 13.0] # Also fine... what ? shouldn't mypy return an error for this ?
我不明白为什么最后一行没有返回错误。对我来说,它看起来像
GenericStringOrFloat = TypeVar("GenericStringOrFloat", str, float)
与
同义GenericStringOrFloat = TypeVar("GenericStringOrFloat", str | float)
因此是完全没有意义的。这可能是我的bug吗?文档看起来这个语法正是为了避免这个问题,并使泛型不是哑Union。
我想要一个排他或,而不是包括,在给我的泛型类型限制的类型之间。用数学的方式表达(尽管不是严格的),我想要一种函子,它可以做类似ListUnionMapper : (A | B) -> List[A] | List[B]
的事情(也适用于Dicts)。
你知道怎么做吗?
=========
编辑:
经过更多的实验,我找到了一个几乎可行的解决方案,但还远远不够完美。TLDR是你需要给泛型变量第二次,以允许mypy进行自动推理/区分;但是泛型类型限制仍然有一些奇怪的地方。
声明变量时,给它指定使用的特定类型(如str
或float
),声明函数签名时,给它指定泛型类型变量。
下面的代码举例说明了我的意思:
from typing import TypeVar, List
GenericStringOrFloat = TypeVar("GenericStringOrFloat", str, float)
ListFloat = List[float]
ListString = List[str]
ListStringOrFloat = List[GenericStringOrFloat]
ListStringOrListFloat = ListString | ListFloat
my_list1A : ListStringOrFloat[ float] = [12.0, 13.0 ] # Good as expected
my_list1B : ListStringOrFloat[ float] = ["hello", "world"] # Error as expected: not float
my_list2A : ListStringOrFloat[str ] = ["hello", "world"] # Good as expected
my_list2B : ListStringOrFloat[str ] = [12.0, 13.0 ] # Error as expected: not str
my_list3 : ListStringOrFloat[str | float] = ["hello", 13.0 ] # Good as expected
my_list4A : ListStringOrListFloat = ["hello", "world"] # Good as expected
my_list4B : ListStringOrListFloat = [12.0, 13.0 ] # Good as expected
my_list4C : ListStringOrListFloat = ["hello", 13.0 ] # Error as expected: not List[str] or List[float]
my_list4B = ["hello", "world"] # NB: doesn't infer/discriminate between List[str] and List[float], since union, so can be reassigned
my_list5A : ListStringOrFloat[int] = ["hello", 13.0 ] # Error as expected: neither List[str} nor List[float]
my_list5B : ListStringOrFloat[int] = [1, 2 ] # UNEXPECTED GOOD: this means that the type restriction basically doesn't work...
my_list_extra : ListStringOrFloat[GenericStringOrFloat] = ["hello", "world"] # Error: Type variable GenericStringOrFloat is unbound
def return_first_element(lst: ListStringOrFloat[GenericStringOrFloat]) -> GenericStringOrFloat:
assert len(lst) > 0
return lst[0]
my_float_A : float = return_first_element(my_list1A) # Good as expected
my_float_B : str = return_first_element(my_list1A) # Error: correctly infers that the function's return type is float
my_str_A : float = return_first_element(my_list2A) # Error: correctly infers that the function's return type is str
my_str_B : str = return_first_element(my_list2A) # Good as expected
my_int_A : float = return_first_element(my_list5B) # Error: expects List[float] (and not List[Generic_Value]...)
my_int_B : str = return_first_element(my_list5B) # Error: expression has type float + expects List[float] (and not List[Generic_Value]...)
my_int_C : int = return_first_element(my_list5B) # Error: expression has type float + expects List[float] (and not List[Generic_Value]...)
问题不在于TypeVar
,而是我如何解释my_list : ListStringOrFloat
。因为没有指定元素类型,所以使用object
。例如,您的代码也通过ListStringOrFloat[object]
,尽管我同意这似乎与GenericStringOrFloat
的含义相矛盾。一般来说,单独的TypeVar
没有什么意义,所以我并不太惊讶它会做一些奇怪的事情。
如果你只使用List[A] | List[B]
,你可以别名ListX = List[A] | List[B]
,然后使用ListX[str, float]
,这会引发["hello", 13.0]
的错误。