是否可以在 Python 中将泛型 C[T] 静态键入为 T?

问题描述

我正在使用 sqlite,其中类型提示仍然远非完美,我最终得到了我认为我想做的 MCVE,它可能与静态类型 ABC 相关。

示例:

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) 不会触发任何类型检查时间错误代码会在运行时使断言失败。

最终,我希望能够有一个 T 类型,我可以将它称为 C[T],这样当我在模型中声明 col_name = Column(String,...) 时,我可以{{ 1}} 或静态检查的一些变体。

这可能吗,如果可能,那么神奇在哪里?

解决方法

如果您最终要将这些列放入模型类中,您可以使用 descriptors(这是 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,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 类,其实际类型定义为 here

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...