为什么在 Mypy 下,
__subclasshook__
适用于 collections.abc
中的一招小马,但不适用于用户定义的类?
例如这个程序
from collections.abc import Hashable
class A:
def __hash__(self) -> int:
return 0
a: Hashable = A()
输出
$ mypy demo.py --strict
Success: no issues found in 1 source file
但是这个等效的程序
from abc import ABCMeta, abstractmethod
def _check_methods(C: type, *methods: str) -> bool:
mro = C.__mro__
for method in methods:
for B in mro:
if method in B.__dict__:
if B.__dict__[method] is None:
return NotImplemented
break
else:
return NotImplemented
return True
class Hashable(metaclass=ABCMeta):
__slots__ = ()
@abstractmethod
def __hash__(self) -> int:
return 0
@classmethod
def __subclasshook__(cls, C: type) -> bool:
if cls is Hashable:
return _check_methods(C, "__hash__")
return NotImplemented
class A:
def __hash__(self) -> int:
return 0
a: Hashable = A()
输出
$ mypy demo.py --strict
demo.py:32: error: Incompatible types in assignment (expression has type "A", variable has type "Hashable")
Found 1 error in 1 file (checked 1 source file)
Mypy 是否以特殊方式处理一招小马?
是的,
mypy
将此类类视为特殊情况。请记住,mypy
用于static类型检查,这意味着它根本不需要运行代码,只需分析源代码即可工作。它实际上从来不会通过 calls __subclasshook__
或类似的方法来确定什么是可哈希的,什么是不可哈希的。您的“等效”类仅在运行时等效,因为它依赖于被调用。如果您希望 __subclasshook__
处理它还不知道的东西,您必须编写一个
mypy
插件来处理它。
包中的规范(“存根文件”)。在这个包中,mypy
是一个
collections.abc.Hashable
。typeshed/stdlib/_collections_abc.pyi
typeshed/stdlib/typing.pyi:
from typing import (
AbstractSet as Set,
AsyncGenerator as AsyncGenerator,
AsyncIterable as AsyncIterable,
AsyncIterator as AsyncIterator,
Awaitable as Awaitable,
ByteString as ByteString,
Callable as Callable,
Collection as Collection,
Container as Container,
Coroutine as Coroutine,
Generator as Generator,
Generic,
Hashable as Hashable,
ItemsView as ItemsView,
Iterable as Iterable,
Iterator as Iterator,
KeysView as KeysView,
Mapping as Mapping,
MappingView as MappingView,
MutableMapping as MutableMapping,
MutableSequence as MutableSequence,
MutableSet as MutableSet,
Reversible as Reversible,
Sequence as Sequence,
Sized as Sized,
TypeVar,
ValuesView as ValuesView,
)