如何让IDE识别Python中静态声明、动态创建类的期望类型名?
问题:
我正在开发一个库,比如支持 UInt5 类型、Int33 类型等等。这个库比这要复杂一些,但为了举例,创建一个 UInt12 类型可能会这样做:
def makeUInt(size:int) -> type[UInt]:
class NewUInt(UInt):
# Do stuff with size
NewUInt.__name__ = f"UInt{size}"
return NewUInt
UInt12 = makeUInt(12)
an_example_number = UInt12(508)
我的开发环境(VS Code)的智能提示功能识别 an_example_number 的类型为 UInt,而不是 UInt12。
麻烦来了:
我并不指望动态声明的类型能被类型提示识别。然而,我已经明确指定了 UInt12 作为类型别名,实际上如果我使用子类而不是类型别名,像这样:
def makeUInt(size:int) -> type[UInt]:
class NewUInt(UInt):
# Do stuff with size
NewUInt.__name__ = f"UInt{size}"
return NewUInt
class UInt12(makeUInt(12)): pass
an_example_number = UInt12(508)
一切就能按预期工作,所以显然在某种程度上,动态声明可以被转化为 IDE 能理解的东西。
举个例子,我可以假设让 UInt 记录已创建的类,并阻止 UInt12(makeUInt(12)) 实际上成为子类。不过,这显然不是一个理想的解决办法。
请求:
我该如何(最好是在 Python 3.8 中)保留动态创建类型的优势,同时让 IDE 理解我对这些类型实例的命名方式?
最终的使用场景是,我想明确提供某些类型,而不需要每次都重新声明 # 使用大小信息做事情,这样像 UInt16、UInt32 等常见类型可以在我的库中声明并获得提示,而像 UInt13 这样的不常见类型则由用户根据需要声明,未必能获得提示。
背景信息:
def makeUInt(size:int) -> type[UInt]:
class NewUInt(UInt):
# Do stuff with size
NewUInt.__name__ = f"UInt{size}"
return NewUInt
UInt12 = makeUInt(12)
an_example_number = UInt12(508)
我希望 an_example_number 在类型提示中显示为 UInt12,但它显示为 UInt。
1 个回答
2
在你的情况下,明确地创建子类可能会更有效,就像你已经看到的那样。
与其创建一个类工厂函数,不如在基类中定义所有派生类的共同行为,然后让具体的实现依赖于子类中定义的类变量。
在一个和你的例子类似的情况下,可以这样写:
class UInt:
size: int # to be defined by subclasses
def __init__(self, value: int):
if value >= 2 ** self.size:
raise ValueError("Too big")
self._value = value
def __repr__(self):
return f"{self.__class__.__name__}({self._value})"
class UInt12(UInt):
size = 12
an_example_number = UInt12(508)
print(an_example_number) # UInt12(508)