带有默认元数据的自定义注释类

问题描述 投票:0回答:2

我正在尝试开发一个自定义的Annotated类...

带注释的允许添加一些元数据到可以在运行时检查的类型提示:

from typing import Annotated

some: Annotated[int, 'must be even']

所以元数据始终是必需的 - 但我想开发一种类似的类型,用一些默认值初始化元数据

some: MyAnnotated[int] # <- this must now must be equal to Annotated[int, '<default-meta>']

我可以使用此代码使其工作:

from typing import Generic, TypeVar, Annotated, Any

T = TypeVar('T')


class MyAnnotated(Generic[T]):
    @classmethod
    def __class_getitem__(cls, param: Any) -> T:
        if isinstance(param, tuple):
            return Annotated[param[0], param[1]]  # type: ignore
        return Annotated[param, '<default-meta>']  # type: ignore


assert MyAnnotated[int, 'my-meta'] == Annotated[int, 'my-meta']
assert MyAnnotated[int] == Annotated[int, '<default-meta>']

这按预期工作 - 但编辑器(VScode)不理解它并且无法添加自动完成功能:

enter image description here

同时与默认的带注释的类一起工作正常:

enter image description here

python python-typing mypy
2个回答
2
投票

这个功能可以直接通过组合

TypeVar
Annotated
来完成:

>>> from typing import TypeVar, Annotated
>>> T = TypeVar('T')
>>> MyAnnotated = Annotated[T, 'must be even']
>>> MyAnnotated
typing.Annotated[~T, 'must be even']
>>> MyAnnotated[int]
typing.Annotated[int, 'must be even']

您提供的链接中的示例:

  • Annotated
    可以与嵌套和通用别名一起使用:
@dataclass
class MaxLen:
    value: int

T = TypeVar("T")
Vec: TypeAlias = Annotated[list[tuple[T, T]], MaxLen(10)]

assert Vec[int] == Annotated[list[tuple[int, int]], MaxLen(10)]

Pycharm能够正确理解,VScode也应该能够: Pycharm correctly understood it


0
投票

我将我的解决方案分为三个不同的部分。最后,我总结了调查结果:

默认元数据的类型别名

实际上,我建议使用类型别名并要求用户使用您的别名来防止代码重复并封装默认值:

# my_annotated.py

from typing import TypeAlias, TypeVar

T = TypeVar("T")
DefaultAnnotated: TypeAlias = Annotated[T, "default-meta"]

def foo(x: DefaultAnnotated[str]):
    x.capitalize()

def bar(x: Annotated[str, "custom-meta"]):
    x.capitalize()

利用

pyi
文件获得更好的 IDE 支持

我理解您希望实现封装并为用户提供单一的注释入口点。因此,您可以使用

pyi
文件来丰富您的解决方案,以通知 IDE 和类型检查器
MyAnnotated
类型也是
Annotated
。它可以帮助 IDE 自动完成,但在使用自定义元数据时与类型检查器不起作用

# my_annotated.pyi

from typing import TypeAlias, TypeVar

T = TypeVar("T")
MyAnnotated: TypeAlias = Annotated[T, "default-meta"]  # Not working for the MyAnnotated[str, "custom-meta"]

The pyi solution informs the IDE for auto-completion but doesn't work with the custom metadata.

此外,不可能利用新的

TypeVarTuple
功能来模拟
Annotated
元数据参数的可变行为(请访问文档了解更多信息):

# my_annotated.pyi

from typing import TypeAlias, TypeVar, TypeVarTuple

T = TypeVar("T")
Vs = TypeVarTuple("Vs")
MyAnnotated: TypeAlias = Annotated[T, *Vs]  # TypeVarTuple doesn't work with the Annotated type

使您的解决方案对 Mypy 友好

不管前面的部分如何,您都可以通过实现一个简单的 Mypy 插件来使您的解决方案对 Mypy 友好:

# mypy.ini or other MyPy configuration-supported files.

[mypy]
plugins = my_annotated_mypy_plugin.py
# my_annotated_mypy_plugin.py

from mypy.plugin import Plugin, AnalyzeTypeContext

def my_annotated_type_analyze_callback(context: AnalyzeTypeContext):
    return context.api.analyze_type(context.type.args[0])

class MyAnnotatedMypyPlugin(Plugin):
    def get_type_analyze_hook(self, fullname: str):
        if fullname.endswith("MyAnnotated"):
            return my_annotated_type_analyze_callback
        return None

def plugin(version: str):
    # ignore version argument if the plugin works with all Mypy versions.
    return MyAnnotatedMypyPlugin

结论

最简单的方法是对默认元数据使用类型别名。但是,如果您仍然喜欢您的解决方案,您可以通过将

pyi
解决方案与 Mypy 插件相结合来增强 IDE 支持并确保与 Mypy 的兼容性。

© www.soinside.com 2019 - 2024. All rights reserved.