在测试来自泛型的继承时如何安静 mypy?

How do I quiet mypy when testing inheritence from a generic?

提问人:2e0byo 提问时间:11/15/2023 更新时间:11/16/2023 访问量:20

问:

以下 MWE 构造泛型类中使用的 typevar 与其在实例上声明的值之间的映射:

from typing import Generic, TypeVar, get_args

T = TypeVar("T")


class Derived(Generic[T]):
    def method(self, val: T) -> T:
        return val


d = Derived[int]()


def get_generic_types_mapping(obj: object) -> dict[type, type]:
    if isinstance(obj, Generic):
        generic_base = next(
            origin
            for origin in obj.__orig_bases__
            if hasattr(origin, "__origin__") and origin.__origin__ is Generic
        )
        return {
            generic: decorated
            for generic, decorated in zip(
                get_args(generic_base), get_args(obj.__orig_class__)
            )
        }
    else:
        return {}


assert get_generic_types_mapping(d) == {T: int}
assert get_generic_types_mapping(object()) == {}

这段代码工作正常,但 mypy(和 pyright)不喜欢它:

$ mypy t.py
t.py:15: error: Argument 2 to "isinstance" has incompatible type "<typing special form>"; expected "_ClassInfo"  [arg-type]
t.py:18: error: "object" has no attribute "__orig_bases__"  [attr-defined]
t.py:24: error: "object" has no attribute "__orig_class__"; maybe "__class__"?  [attr-defined]
Found 3 errors in 1 file (checked 1 source file)

这是有道理的,因为对象实际上没有Generic__orig_bases__

from typing import Generic

assert not hasattr(Generic(), "__orig_bases__")

我怎么告诉mypy确实有?[我读什么才能理解它来自哪里?我应该使用其他东西吗?obj__orig_bases__isinstance

(我只想到了 ,但这并不能消除 上的错误,如果泛型没有定义的类型,我实际上想引发错误。我可以同时检查两者,但我觉得我在概念上遗漏了一些东西。if hasattr(obj, "__orig_bases__")__orig_class__

Python 泛型 类型

评论


答:

0赞 2e0byo 11/16/2023 #1

简短的回答是 mypy 在这里非常正确。 由 CPython 在此处设置,但操作容易出错:它失败(例如,如果目标类使用 ),则该属性不可用。GenericAlias.__orig_class__typing__slots__

因此,不幸的是,正确的做法是检查这两个属性并处理其他情况。由于我可以访问我关心的任何类的调用签名(通过或通过显式传递),因此我可以将它们设置为自定义私有属性并改用它。__orig_class__

有关更多信息,请参阅上面链接的答案和来源。