是否可以将泛型 C[T] 静态键入为 Python 中的 T?
Is it possible to statically type a generic C[T] as T in Python?
我正在使用 SQLite,其中类型提示还远非完美,我最终得到了一个我认为我想做的 MCVE,它可能与静态类型 ABCs 有关。
示例:
T = TypeVar('T')
class ColumnarValue(Generic[T]):
pass
def test1(guard: ColumnarValue[str]): ...
def test2(guard: ColumnarValue[int]): ...
# These should work
test1("Hello")
test2(42)
# These should be type errors
test1(42)
test2("Hello")
# Or, likewise:
test_assignment_1: ColumnarValue[str] = "this line should typecheck"
test_assignment_2: ColumnarValue[int] = "this line should NOT typecheck"
我尝试使用 ABC,但由于它们似乎使用 ABC.register() 进行注册,所以类型检查器不知道我在说什么。
我也尝试过通用协议,但我必须设计一个协议来保护每种类型 T 所期望的功能。
我正在用 Pyright/Pylance 对此进行测试,但如果这是问题的一部分,则替代方案将被视为答案。
我还发现 https://github.com/antonagestam/phantom-types,根据文档,它“不会增加任何处理或内存开销”。这并不完全正确,因为它们在运行时通过 .parse 和 __instancecheck__
协议(尽管它们在技术上不创建任何新对象,但它们在堆栈帧中确实有一些开销)。
唉,这不是静态检查器,虽然那里的解决方案似乎是使用 assert isinstance("hello", MyStr)
来确保类型检查器可以在该行之后满足此类保证(这至少在 mypy 和 pylance 中有效) ,它不会触发 assert isinstance("hello", MyInt)
的任何类型检查时间错误。代码会在运行时断言失败。
最终,我希望能够拥有一个可以称为 C[T]
的类型 T
,这样当我在模型中声明一个 col_name = Column(String, ...)
时,我就可以col_name = "value"
或经过静态检查的它的一些变体。
这可能吗,如果可能的话,神奇在哪里?
如果您最终要将这些列放入模型中 class,您可以使用 descriptors (which is what e.g. the property
装饰器,如果您熟悉的话)。
草图:
from typing import TypeVar, Generic, Any
T = TypeVar("T")
class ColumnarValue(Generic[T]):
def __get__(self, instance: object, owner: Any) -> T:
...
def __set__(self, instance: object, value: T) -> None:
...
class Model:
some_str_column: ColumnarValue[str]
some_int_column: ColumnarValue[int]
m = Model()
m.some_str_column = "this line typechecks"
m.some_int_column = "this line does NOT typecheck"
另请查看 sqlalchemy 如何处理所有这些 here。
特别是 Mapped
class, with its actual type definition here.
我正在使用 SQLite,其中类型提示还远非完美,我最终得到了一个我认为我想做的 MCVE,它可能与静态类型 ABCs 有关。
示例:
T = TypeVar('T')
class ColumnarValue(Generic[T]):
pass
def test1(guard: ColumnarValue[str]): ...
def test2(guard: ColumnarValue[int]): ...
# These should work
test1("Hello")
test2(42)
# These should be type errors
test1(42)
test2("Hello")
# Or, likewise:
test_assignment_1: ColumnarValue[str] = "this line should typecheck"
test_assignment_2: ColumnarValue[int] = "this line should NOT typecheck"
我尝试使用 ABC,但由于它们似乎使用 ABC.register() 进行注册,所以类型检查器不知道我在说什么。
我也尝试过通用协议,但我必须设计一个协议来保护每种类型 T 所期望的功能。
我正在用 Pyright/Pylance 对此进行测试,但如果这是问题的一部分,则替代方案将被视为答案。
我还发现 https://github.com/antonagestam/phantom-types,根据文档,它“不会增加任何处理或内存开销”。这并不完全正确,因为它们在运行时通过 .parse 和 __instancecheck__
协议(尽管它们在技术上不创建任何新对象,但它们在堆栈帧中确实有一些开销)。
唉,这不是静态检查器,虽然那里的解决方案似乎是使用 assert isinstance("hello", MyStr)
来确保类型检查器可以在该行之后满足此类保证(这至少在 mypy 和 pylance 中有效) ,它不会触发 assert isinstance("hello", MyInt)
的任何类型检查时间错误。代码会在运行时断言失败。
最终,我希望能够拥有一个可以称为 C[T]
的类型 T
,这样当我在模型中声明一个 col_name = Column(String, ...)
时,我就可以col_name = "value"
或经过静态检查的它的一些变体。
这可能吗,如果可能的话,神奇在哪里?
如果您最终要将这些列放入模型中 class,您可以使用 descriptors (which is what e.g. the property
装饰器,如果您熟悉的话)。
草图:
from typing import TypeVar, Generic, Any
T = TypeVar("T")
class ColumnarValue(Generic[T]):
def __get__(self, instance: object, owner: Any) -> T:
...
def __set__(self, instance: object, value: T) -> None:
...
class Model:
some_str_column: ColumnarValue[str]
some_int_column: ColumnarValue[int]
m = Model()
m.some_str_column = "this line typechecks"
m.some_int_column = "this line does NOT typecheck"
另请查看 sqlalchemy 如何处理所有这些 here。
特别是 Mapped
class, with its actual type definition here.