python - Python 3.7中使用工厂函数生成注释类型时出现 “typing.ClassVar”问题

标签 python python-3.x tuples mypy python-typing

我正在尝试使用工厂函数来生成一些类型注释 - 特别是针对 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

…在此更改之后,代码甚至不会最初编译 - 它无法通过 typing 模块深处的 TypeError 进行编译:

TypeError: typing.ClassVar[typing.Tuple[~HomogenousTypeVar, ...]] is not valid as type argument

…随着错误的发生,我觉得这有点像电话进来的;我的意思是,难道打字中的一切都应该以某种方式成为有效的类型参数吗?

the typing source code related to ClassVar ,文档字符串中提到了对其使用的一些限制 - 但这不是其中之一。我是否缺少一些明显的东西?我尝试以这种方式使用此注释是否是堂吉诃德式的?我还可以尝试什么?

最佳答案

您确定您的原始代码片段确实使用 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 语言。

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

<小时/>

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

例如,在本例中,您试图概括元组类型 - 这使我推断您的代码库包含许多不同种类的不同数量和类型的元组。

这让我觉得有点可疑——我会考虑将其中一些元组转换为常规类或(如果您仍然需要类似元组的功能)namedtupleDataclasses (这是新的 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: ...

关于python - Python 3.7中使用工厂函数生成注释类型时出现 “typing.ClassVar”问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52262822/

相关文章:

Python 循环引用问题 :

python-3.x - Unicode解码错误: 'utf-8' codec can't decode byte 0xea in position 23: invalid continuation byte

python - 如何在 Python 中按每个元组的某个索引的整数值对元组列表进行排序?

python - 为什么更新元组中的集合会导致错误?

tuples - Python 2 中的扩展元组解包

Python isDisjoint() 运行时

python - Pygame:如何多态地编写事件循环

python - 在 Flask + SQLAlchemy 中设置一对一关系

python-3.x - cv2 - 用噪声掩盖图像

python - 如何在 python PyOpenGL 中旋转魔方的切片?