gpt4 book ai didi

cython - mypy 不喜欢别名 Cython 类型

转载 作者:行者123 更新时间:2023-12-04 02:37:02 34 4
gpt4 key购买 nike

我正在尝试使用 Cython 加速 PEP 484 类型的 python 脚本。我想保持一些语义和可读性。

之前,我有一个

Flags = int

def difference(f1: Flags, f2: Flags):
return bin(f1 ^ f2).count("1")

现在这个函数经常被调用,并且是使用 Cython 在 C 中进行轻微重构和编译的自然候选者,但我不想丢失 f1 的信息。和 f2是标志的集合。所以,我显然尝试过
import cython

Flags = cython.int

def difference(f1: Flags, f2: Flags):
return bin(f1 ^ f2).count("1")

现在, mypy失败了,提示
flags.py:5: error: Variable "flags.Flags" is not valid as a type
flags.py:5: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
flags.py:6: error: Unsupported left operand type for ^ (Flags?)

而没有该类型别名
import cython

def difference(f1: cython.int, f2: cython.int):
return bin(f1 ^ f2).count("1")

该模块检查得很好(除了 cython 的缺少库 stub )。

这里发生了什么?类型别名的意义不就是不应该在行为上没有区别吗?

最佳答案

您在这里遇到的问题是,由于没有与 cython 相关的类型提示,不幸的是,表达式 cython.int 的确切含义是模棱两可的。应该是--因此模棱两可Flags = cython.int应该是这个意思。

特别是,cython.int 可能是这种情况。应该是一个值,而不是一个类型。在这种情况下,Flags = cython.int将只是一个常规变量赋值,而不是类型别名。

虽然 mypy 理论上可以尝试分析程序的其余部分来解决这种歧义,但这样做会有些昂贵。因此,它有点武断地决定 cython.int必须是一个值(例如常量),这反过来会导致您的 difference功能无法进行类型检查。

但是,如果您使用 cython.int type 直接在类型签名中,我们不会得到这样的歧义:在这种情况下,该表达式很可能意味着某种类型,因此 mypy 决定以另一种方式解释该表达式。

那么,你如何解决这个问题?好吧,有几件事你可以尝试,我将按照大致递减的顺序(以及递增的 hackyness 顺序)列出。

  • 向 mypy 提交拉取请求以实现对 PEP 613 的支持.此 PEP 旨在为用户提供一种直接解决这种歧义的方法,让他们直接指示某事物是否应该是类型别名。

    此 PEP 已被接受; mypy 不支持它的唯一原因是因为还没有人开始实现它。
  • 询问 Cython 维护者是否可以通过将他们的包转换为 PEP 561 来为 cython 运送 stub 文件。兼容包——与类型提示捆绑在一起的包。

    似乎 Cython 已经在 limited way 中捆绑了一些类型提示。 , 并且让它们可供外部使用理论上可能就像测试以确保它们仍然是最新的并添加 py.typed 一样简单。文件到 Cython 包。

    有关 Cython 中类型提示的更多上下文可以找到 herehere .

    Mypy 还计划 overhauling how imports are handled因此,即使软件包在接下来的几个月内不符合 PEP 561,您也可以选择使用任何捆绑的类型提示——您也可以等待这种情况发生。
  • 为 cython 创建自己的 stub 包。此包可能不完整,仅定义 int以及您需要的其他一些东西。例如,您可以创建一个如下所示的“stubs/cython.pyi”文件:

    from typing import Any

    # Defining these two functions will tell mypy that this stub file
    # is incomplete and to not complain if you try importing things other
    # than 'int'.
    def __getattr__(name: str) -> Any: ...
    def __setattr__(name: str, value: Any) -> None: ...

    class _int:
    # Define relevant method stubs here

    然后,除了您通常的代码之外,将 mypy 指向这个 stub 文件。 Mypy 会明白它应该使用这个 stub 文件作为 cython 的类型提示。模块。这意味着当您执行 cython.int ,mypy 将看到它是您在上面定义的类,因此将有足够的信息知道 Flags = cython.int很可能是类型别名。
  • 重新定义Flags仅在执行类型检查时分配。您可以通过 typing.TYPE_CHECKING 进行此操作多变的:

    from typing import TYPE_CHECKING
    import cython

    # The TYPE_CHECKING variable is always False at runtime, but is treated
    # as being always True for the purposes of type checking
    if TYPE_CHECKING:
    # Hopefully this is a good enough approximation of cython.int?
    Flags = int
    else:
    Flags = cython.int

    def difference(f1: Flags, f2: Flags):
    return bin(f1 ^ f2).count("1")

    这种方法的一个警告是,我不确定 Cython 在多大程度上支持这些 PEP 484 技巧,以及它是否会识别 Flags如果它包含在这样的 if 语句中,则意味着它是一个类型别名。
  • 而不是制作Flags cython.int 的类型别名,使其成为子类:

    import cython

    class Flags(cython.int): pass

    def foo(a: Flags, b: Flags) -> Flags:
    return a ^ b

    现在,您正在使用 cython.int回到可以合理地假设它是一种类型的上下文中,并且 mypy 最终不会报告错误。

    当然,这确实会改变程序的语义,也可能会让 Cython 不高兴——我不太熟悉 Cython 的工作原理,但我怀疑你并不是真的要继承 cython.int .
  • 关于cython - mypy 不喜欢别名 Cython 类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61237827/

    34 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com