如何使用一次性泛型创建类型别名?
How to create a type alias with a throw-away generic?
我希望创建一个类型别名,它可以采用别名中实际未使用的泛型。原因是为了自记录代码,我知道没有类型检查器能够实际检查它。这就是我的意思:
from typing import TypeVar, Dict, Any
from dataclasses import dataclass
from dataclasses_json import dataclass_json, DataClassJsonMixin
JSON = Dict[str, Any] # Good enough json type for this demo
T = TypeVar("T", bound=DataClassJsonMixin)
JSONOf = JSON[T] # <-- mypy Problem: JSON takes no generic arguments
@dataclass_json # this just provides to_json/from_json to dataclasses, not important for question
@dataclass
class Person:
name: str
age: int
def add_person_to_db(person: JSONOf[Person]):
# just from reading the signature the user now knows what the input is
# If I just put JSON as a type, it is unclear
...
问题是JSON类型别名没有使用泛型参数,所以收不到。我需要在别名定义中使用它而不用实际对它做任何事情。我尝试使用 PEP-593 Annotated 类型执行此操作,但它也不起作用(我可以像下面这样定义它,但它仍然不期望通用参数):
from typing import Annotated
JSONOf = Annotated[JSON, T]
我需要做类似的事情(这不存在):
from typing import ThrowAwayGeneric
JSONOf = ThrowAwayGeneric[JSON, T]
有没有可能得到类似于类型检查的东西?再一次:我实际上对 json 数据的类型检查不感兴趣,我只是想提供一个可读的函数签名。如果完全没问题,在实际类型检查时使用 Dict[str, Any]
。
那种蛮力方法当然是为每种类型定义一个别名,但是由于有很多这样的类型,这变得很乏味。
JSONOfPerson = JSON
我想出了一些解决方案:
- 将
JSONOf
与另一种采用泛型的类型合并,将这种类型设计成一种永远不会匹配任何东西的方式,例如带有可变键的 Dict
:
JSONOf = Union[JSON, Dict[List, T]] # List could be any mutable, including JSON itself
- 相同的方法,结合自定义泛型 class:
class JSONFrom(Generic[T]): pass # Name was chosen to look nice on mypy errors
JSONOf = Union[JSON, JSONFrom[T]]
我们来测试一下:
from typing import Generic, TypeVar, Dict, Any, Union
class DataClassJsonMixin: ...
class Person(DataClassJsonMixin): ...
JSON = Dict[str, Any] # Good enough json type for this demo
T = TypeVar("T", bound=DataClassJsonMixin)
# Solution 1: Union with impossible type that takes a Generic
# JSONOf = Union[JSON, Dict[JSON, T]]
# Solution 2: Union with custom generic class
class JSONFrom(Generic[T]):
# just a crude failsafe to prohibit instantiation
# could be improved with __new__, __init_subclasses__, etc
def __init__(self):
raise TypeError("Just dont!")
JSONOf = Union[JSON, JSONFrom[T]]
def add_person_to_db(person: JSONOf[Person]):
print(person)
try: reveal_type(person)
except NameError: pass
add_person_to_db({'id': 1234, 'name': 'Someone'}) # Checks
add_person_to_db("Not a JSON") # Check error by JSON
add_person_to_db(Person()) # Make sure it does not accept Person
try: someone: JSONFrom = JSONFrom() # Make sure it does not accept JSONFrom
except TypeError as e: print(e) # Nice try!
$ python3 test.py
{'id': 1234, 'name': 'Someone'}
Not a JSON
<__main__.Person object at 0x7fa258a7d128>
Just dont!
$ mypy test.py
test.py:22: note: Revealed type is "Union[builtins.dict[builtins.str, Any], test.JSONFrom[test.Person]]"
test.py:26: error: Argument 1 to "add_person_to_db" has incompatible type "str"; expected "Union[Dict[str, Any], JSONFrom[Person]]"
test.py:27: error: Argument 1 to "add_person_to_db" has incompatible type "Person"; expected "Union[Dict[str, Any], JSONFrom[Person]]"
Found 2 errors in 1 file (checked 1 source file)
我希望创建一个类型别名,它可以采用别名中实际未使用的泛型。原因是为了自记录代码,我知道没有类型检查器能够实际检查它。这就是我的意思:
from typing import TypeVar, Dict, Any
from dataclasses import dataclass
from dataclasses_json import dataclass_json, DataClassJsonMixin
JSON = Dict[str, Any] # Good enough json type for this demo
T = TypeVar("T", bound=DataClassJsonMixin)
JSONOf = JSON[T] # <-- mypy Problem: JSON takes no generic arguments
@dataclass_json # this just provides to_json/from_json to dataclasses, not important for question
@dataclass
class Person:
name: str
age: int
def add_person_to_db(person: JSONOf[Person]):
# just from reading the signature the user now knows what the input is
# If I just put JSON as a type, it is unclear
...
问题是JSON类型别名没有使用泛型参数,所以收不到。我需要在别名定义中使用它而不用实际对它做任何事情。我尝试使用 PEP-593 Annotated 类型执行此操作,但它也不起作用(我可以像下面这样定义它,但它仍然不期望通用参数):
from typing import Annotated
JSONOf = Annotated[JSON, T]
我需要做类似的事情(这不存在):
from typing import ThrowAwayGeneric
JSONOf = ThrowAwayGeneric[JSON, T]
有没有可能得到类似于类型检查的东西?再一次:我实际上对 json 数据的类型检查不感兴趣,我只是想提供一个可读的函数签名。如果完全没问题,在实际类型检查时使用 Dict[str, Any]
。
那种蛮力方法当然是为每种类型定义一个别名,但是由于有很多这样的类型,这变得很乏味。
JSONOfPerson = JSON
我想出了一些解决方案:
- 将
JSONOf
与另一种采用泛型的类型合并,将这种类型设计成一种永远不会匹配任何东西的方式,例如带有可变键的Dict
:
JSONOf = Union[JSON, Dict[List, T]] # List could be any mutable, including JSON itself
- 相同的方法,结合自定义泛型 class:
class JSONFrom(Generic[T]): pass # Name was chosen to look nice on mypy errors
JSONOf = Union[JSON, JSONFrom[T]]
我们来测试一下:
from typing import Generic, TypeVar, Dict, Any, Union
class DataClassJsonMixin: ...
class Person(DataClassJsonMixin): ...
JSON = Dict[str, Any] # Good enough json type for this demo
T = TypeVar("T", bound=DataClassJsonMixin)
# Solution 1: Union with impossible type that takes a Generic
# JSONOf = Union[JSON, Dict[JSON, T]]
# Solution 2: Union with custom generic class
class JSONFrom(Generic[T]):
# just a crude failsafe to prohibit instantiation
# could be improved with __new__, __init_subclasses__, etc
def __init__(self):
raise TypeError("Just dont!")
JSONOf = Union[JSON, JSONFrom[T]]
def add_person_to_db(person: JSONOf[Person]):
print(person)
try: reveal_type(person)
except NameError: pass
add_person_to_db({'id': 1234, 'name': 'Someone'}) # Checks
add_person_to_db("Not a JSON") # Check error by JSON
add_person_to_db(Person()) # Make sure it does not accept Person
try: someone: JSONFrom = JSONFrom() # Make sure it does not accept JSONFrom
except TypeError as e: print(e) # Nice try!
$ python3 test.py
{'id': 1234, 'name': 'Someone'}
Not a JSON
<__main__.Person object at 0x7fa258a7d128>
Just dont!
$ mypy test.py
test.py:22: note: Revealed type is "Union[builtins.dict[builtins.str, Any], test.JSONFrom[test.Person]]"
test.py:26: error: Argument 1 to "add_person_to_db" has incompatible type "str"; expected "Union[Dict[str, Any], JSONFrom[Person]]"
test.py:27: error: Argument 1 to "add_person_to_db" has incompatible type "Person"; expected "Union[Dict[str, Any], JSONFrom[Person]]"
Found 2 errors in 1 file (checked 1 source file)