1

我正在尝试使用工厂函数来生成一些类型注释——特别是针对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深处的a 来编译:typing

TypeError: typing.ClassVar[typing.Tuple[~HomogenousTypeVar, ...]] 作为类型参数无效

......随着错误的发生,我觉得有点像打电话;我的意思是,不是所有东西都应该typing以某种方式是有效的类型参数吗?

在与 相关typing源代码ClassVar中,文档字符串中提到了一些对其使用的限制——但这不是其中之一。我有什么明显的遗漏吗?我以这种方式使用此注释的尝试是不切实际的吗?我还能尝试什么?

4

1 回答 1

1

你确定你的原始代码片段实际上是用 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-install mypy 并运行python3 -m mypy test.py.


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

因此,这意味着不幸的是,如果您希望符合 PEP 484 的工具能够分析您的代码,那么您将无法将生成的类型提示与 ClassVars 一起使用——它们无法理解/解释您的原始类型提示集,并且添加 ClassVars 肯定无济于事。

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

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


更广泛地说,每当你开始遇到这些类型相关的限制时,我认为这表明你的代码太复杂了。我要么考虑简化你的代码,要么(如果不可能的话)切换到像 Haskell 或 Idris 这样的语言,它可以让你使用更具表现力(尽管更复杂)的类型系统。

例如,在这种情况下,您试图概括 Tuple 类型——这导致我推断您的代码库包含许多不同类型的元组和不同类型的元组。

这让我觉得有点可疑——我会考虑将其中一些元组转换为常规类或(如果您仍然需要类似元组的功能)一个namedtuple数据类(从 Python 3.7开始是新的)在这里也很方便。

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

或者,如果您只有几种不同类型的元组但在所有地方都使用这些元组,您可以尝试使用类型别名,这样您就不必一遍又一遍地重复重新键入相同的(长)类型。例如,而不是这样做:

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

...你可以这样做:

IpAddress = Tuple[int, int, int, int]

def foo(x: IpAddress) -> None: ...
于 2018-09-11T03:47:43.333 回答