代码之家  ›  专栏  ›  技术社区  ›  fish2000

使用工厂函数在python 3.7中生成注释类型时,__typing.classvar_出现问题

  •  1
  • fish2000  · 技术社区  · 6 年前

    我正在尝试使用工厂函数生成一些类型注释,专门用于 tuple 类型。我有一个版本的工厂运行良好(如中所述,它在mypy中编译、运行并满意地签出):

    import typing as tx
    HomogenousTypeVar = tx.TypeVar('HomogenousTypeVar')
    TupleTypeReturnType = tx.Type[tx.Tuple[HomogenousTypeVar, ...]]
    
    def TupleType(length: int,
                  tuptyp: tx.Type[HomogenousTypeVar] = str) -> TupleTypeReturnType:
        """ Create a type annotation for a tuple of a given type and length """
        assert length > 0
        return tx.Tuple[tuple(tuptyp for idx in range(length))]
    

    _,其用法类似于:

    class Thing(object):
    
        __slots__: TupleType(2) = ('yo', 'dogg')
        other_fields: TupleType(4) = ('i', 'heard',
                                      'you', 'like')
    
        # etc, or what have you
    

    然而,当我试图增加对 typing.ClassVar 注释,如下所示:

    import typing as tx
    HomogenousTypeVar = tx.TypeVar('HomogenousTypeVar')
    TupleTypeReturnType = tx.Union[tx.Type[tx.Tuple[HomogenousTypeVar, ...]],
                                   tx.Type[tx.ClassVar[tx.Tuple[HomogenousTypeVar, ...]]]]
    
    def TupleType(length: int,
                  tuptyp: tx.Type[HomogenousTypeVar] = str,
                  clsvar: bool = False) -> TupleTypeReturnType:
        """ Create a type annotation for a tuple of a given type and length,
            specifying additionally whether or not it is a ClassVar """
        assert length > 0
        out = tx.Tuple[tuple(tuptyp for idx in range(length))]
        return clsvar and tx.ClassVar[out] or out
    

    _此更改后,代码甚至不会在最初编译_时进行编译,但使用 TypeError 从内心深处 typing 模块:

    类型错误:typing.classvar[typing.tuple[~homoustypevar,…]]是 作为类型参数无效

    _随着错误的发生,它在我的脑海中闪现,就像一个小电话;我的意思是,它不是 一切 在里面 打字 应该是某种形式的有效类型参数,是给予还是接受?

    the typing source code related to ClassVar ,在docstring_“中提到了对其使用的一些限制,但这不是其中之一。有什么明显的我不见了吗?我试图用这种方式使用这个注释是一个不切实际的注释吗?我还可以试试什么?

    1 回复  |  直到 6 年前
        1
  •  1
  •   Michael0x2a    6 年前

    您确定您的原始代码片段实际上是用mypy进行类型检查吗?当我尝试使用mypy 0.620或Github的最新版本运行它时,我得到以下错误:

    test.py:13: error: invalid type comment or annotation
    test.py:13: note: Suggestion: use TupleType[...] instead of TupleType(...)
    test.py:14: error: invalid type comment or annotation
    test.py:14: note: Suggestion: use TupleType[...] instead of TupleType(...)
    

    我也无法重现用ClassVar代码得到的错误——当我尝试运行它时,我得到以下错误:

    test.py:4: error: Invalid type: ClassVar nested inside other type
    test.py:6: error: Incompatible default for argument "tuptyp" (default has type "Type[str]", argument has type "Type[HomogenousTypeVar]")
    test.py:12: error: Invalid type alias
    test.py:13: warning: Returning Any from function declared to return "Union[Type[Tuple[HomogenousTypeVar?, ...]], Type[Tuple[HomogenousTypeVar?, ...]]]"
    test.py:15: error: Name 'Thing' is not defined
    test.py:16: error: Revealed type is 'Any'
    

    你确定你在运行mypy,而不是仅仅运行代码吗?例如,如果你只跑 python3 test.py ,您基本上跳过了所有类型检查(除了在类型模块中内置的一些简单的最低健全性检查)。

    如果要键入检查代码,则需要pip安装mypy并运行 python3 -m mypy test.py .


    在任何情况下,所有这些错误消息都是预期行为——mypy(和任何其他符合PEP 484的类型检查器)只能分析代码 静态地 并且不会尝试运行或分析任何工厂函数/任何类型的提示生成函数。

    因此,这意味着不幸的是,如果您希望符合PEP484的工具能够分析您的代码,那么您将无法将生成的类型提示与类变量一起使用——它们无法理解/解释您的原始类型提示集,并且添加类变量当然没有帮助。

    如果您想生成类型提示,我能想到的唯一真正的选择是在python上创建某种小型语言或宏系统,当运行时,它将生成python代码。然后运行并类型检查生成的代码,而不是宏化的Python语言。

    但我真的不建议这样做——这是一个非常脆弱的黑客。


    更广泛地说,每当您遇到与类型相关的这些限制时,我认为这是您的代码过于复杂的一个迹象。我或者考虑简化代码,或者(如果不可能的话)切换到像haskell或idris这样的语言,这样可以使用更具表现力(尽管更复杂)的类型系统。

    例如,在本例中,您试图推广元组类型——这导致我推断您的代码基包含许多不同类型的具有不同算术和类型的元组。

    这让我觉得有点可疑——我会转而考虑将其中一些元组转换为常规类或(如果您仍然需要类似于元组的功能)a namedtuple . Dataclasses (新的) as of Python 3.7 )在这里也很方便。

    这些解决方案还有助于使代码更具可读性——现在您可以为每种不同的元组赋予具体的名称和含义。

    或者,如果您只有几个不同类型的元组,但在各处都使用这些元组,则可以尝试使用 type aliases 因此,您不必反复地重新键入相同的(长)类型。例如,不要这样做:

    def foo(x: Tuple[int, int, int, int]) -> None: ...
    

    …你可以这样做:

    IpAddress = Tuple[int, int, int, int]
    
    def foo(x: IpAddress) -> None: ...