我正在使用 pylance 并启用 严格模式 ,并希望获得更好的开发体验。
它工作得很好,直到我定义了一些 类装饰器
def struct(cls : Type[Any]) -> Type[Any]:
# ... do some magic here ...
return dataclass(frozen=True)(cls)
@struct
class Vec:
x: int
y: int
print(Vec(1, "abc")) # no error msg here, no hints about constructor arguments also.
这里,当我输入
Vec(
时,没有关于构造函数参数类型的提示,当我输入
Vec(1, "abc")
时,没有发生类型错误。
并且我发现将
@struct
定义为泛型函数(而不是使用 Any)使事情变得更糟:
A = TypeVar("A")
def struct(cls : Type[A]) -> Type[A]:
# ... do some magic here ...
return dataclass(frozen=True)(cls)
@struct
class Vec:
x: int
y: int
print(Vec(1, 2)) # type error here: Expected no arguments to Vec
在这种情况下,当我输入
Vec(1, 2)
时,会发生类型错误,并且显示“Expected no arguments to Vec”,这不是预期的。|| |我希望有某种方式可以
告诉 pylance
(或其他静态 linter)有关返回类的
元信息
(可能通过
从原始类生成,但我承诺,返回的类的元数据在此之后不会动态修改)。
typing.get_type_hints
我注意到 pylance 可以很好地处理
,所以我认为可能有
@dataclass
某种机制| ||来实现这一点。
有什么办法可以做到这一点吗?或者
只是经过pylance特殊处理的?
Is there any way to do that? or
@dataclass
is just special processed by pylance?
遇到的问题是 Pylance(以及 MyPy 等其他类型检查器)难以推断出类装饰器的返回类型,尤其是在涉及运行时类型操作时。
以下是一些可以使的类装饰器对 Pylance 更友好的方法:
1. 使用
typing.cast
进行类型断言:
虽然这不是最优雅的解决方案,但它简单有效。可以使用
typing.cast
告诉 Pylance 装饰器返回的类型:
from typing import TypeVar, Type, Any, cast
from dataclasses import dataclass
A = TypeVar("A")
def struct(cls: Type[A]) -> Type[A]:
# ... do some magic here ...
dataclass_cls = dataclass(frozen=True)(cls)
return cast(Type[A], dataclass_cls)
@struct
class Vec:
x: int
y: int
print(Vec(1, 2)) # Now Pylance should recognize the constructor arguments
2. 使用
__post_init__
初始化数据类属性:
与其在装饰器中使用
dataclass(frozen=True)
,不如在类定义中使用它,并在
__post_init__
方法中初始化冻结的属性:
from typing import TypeVar, Type, Any
from dataclasses import dataclass, field
A = TypeVar("A")
def struct(cls: Type[A]) -> Type[A]:
# ... do some magic here ...
return cls
@struct
@dataclass(frozen=True)
class Vec:
x: int
y: int
def __post_init__(self):
# ... 在此处进行任何必要的初始化 ...
print(Vec(1, 2)) # Pylance 应该能够推断出构造函数参数
3. 使用自定义元类:
可以定义一个自定义元类,它在创建类时应用的逻辑,并确保类型提示正确传播:
from typing import TypeVar, Type, Any
A = TypeVar("A")
class StructMeta(type):
def __new__(cls, name, bases, attrs):
# ... 在此处应用的逻辑 ...
return super().__new__(cls, name, bases, attrs)
class Vec(metaclass=StructMeta):
x: int
y: int
print(Vec(1, 2)) # Pylance 应该能够推断出构造函数参数
请注意,这些方法各有优缺点,需要根据具体情况选择最适合的方法。
不幸的是,
@dataclass
装饰器确实经过 Pylance 特殊处理,以提供更好的类型推断。 虽然目前没有直接的方法可以获得与自定义类装饰器相同的级别,但这三种方法可以帮助提高 Pylance 对的代码的理解,并提供更好的开发体验.