用Mypy键入dict mixin类

时间:2019-04-12 17:57:58

标签: python dictionary multiple-inheritance mixins mypy

我正在尝试编写一个小的mixin类来桥接Set和MutableMapping类型:我希望映射类型能够接收一些对象(字节),对它们进行哈希处理并存储它们,以便它们可以访问哈希。

这是将此类与标准dict混合使用的工作版本:

from hashlib import blake2b

class HashingMixin:
    def add(self, content):
        digest = blake2b(content).hexdigest()
        self[digest] = content

class HashingDict(dict, HashingMixin):
    pass

但是我不知道如何添加类型注释。

https://github.com/python/mypy/issues/1996看来,mixin必须继承abc.ABCabc.abstractmethod的子类,以定义它期望调用的所有方法,所以这是我的镜头:

import abc
from hashlib import blake2b
from typing import Dict

class HashingMixin(abc.ABC):
    def add(self, content: bytes) -> None:
        digest = blake2b(content).hexdigest()
        self[digest] = content

    @abc.abstractmethod
    def __getitem__(self, key: str) -> bytes:
        raise NotImplementedError

    @abc.abstractmethod
    def __setitem__(self, key: str, content: bytes) -> None:
        raise NotImplementedError


class HashingDict(Dict[str, bytes], HashingMixin):
    pass

然后Mypy抱怨HashingDict的定义:

error: Definition of "__getitem__" in base class "dict" is incompatible with definition in base class "HashingMixin"
error: Definition of "__setitem__" in base class "dict" is incompatible with definition in base class "HashingMixin"
error: Definition of "__setitem__" in base class "MutableMapping" is incompatible with definition in base class "HashingMixin"
error: Definition of "__getitem__" in base class "Mapping" is incompatible with definition in base class "HashingMixin"

公开类型:

reveal_type(HashingMixin.__getitem__)
reveal_type(HashingDict.__getitem__)

产量:

error: Revealed type is 'def (coup.content.HashingMixin, builtins.str) -> builtins.bytes'
error: Revealed type is 'def (builtins.dict[_KT`1, _VT`2], _KT`1) -> _VT`2'

我不知道怎么了:(

1 个答案:

答案 0 :(得分:1)

这似乎是mypy中的错误-请参阅mypy用于使用多重继承分析类的MRO的代码中的this TODO。简而言之,mypy会错误地完成忽略忽略您已使用具体值对Dict进行参数化的功能,而是像在使用Dict一样对代码进行分析。

我相信https://github.com/python/mypy/issues/5973可能是问题跟踪器中最相关的问题:根本原因是相同的。

在此错误修复之前,您可以通过在任何有错误的行上添加# type: ignore来抑制mypy在该行上生成的错误。因此,您可以执行以下操作:

import abc
from hashlib import blake2b
from typing import Dict

class HashingMixin(abc.ABC):
    def add(self, content: bytes) -> None:
        digest = blake2b(content).hexdigest()
        self[digest] = content

    @abc.abstractmethod
    def __getitem__(self, key: str) -> bytes:
        raise NotImplementedError

    @abc.abstractmethod
    def __setitem__(self, key: str, content: bytes) -> None:
        raise NotImplementedError


class HashingDict(Dict[str, bytes], HashingMixin):  # type: ignore
    pass

如果您决定采用这种方法,我建议您还留下一条注释,记录为什么您要抑制这些错误并使用--warn-unused-ignores标志运行mypy。

前者是为了让您的代码的任何将来的读者受益;后者会在遇到# type: ignore并没有实际抑制任何错误的Launching lib\main.dart on Android SDK built for x86 in debug mode... Initializing gradle... Resolving dependencies... Running Gradle task 'assembleDebug'... registerResGeneratingTask is deprecated, use registerGeneratedResFolders(FileCollection) registerResGeneratingTask is deprecated, use registerGeneratedResFolders(FileCollection) registerResGeneratingTask is deprecated, use registerGeneratedResFolders(FileCollection) registerResGeneratingTask is deprecated, use registerGeneratedResFolders(FileCollection) registerResGeneratingTask is deprecated, use registerGeneratedResFolders(FileCollection) Built build\app\outputs\apk\debug\app-debug.apk. I/OpenGLRenderer( 5789): Davey! duration=865ms; Flags=1, IntendedVsync=2354083756924, Vsync=2354517090240, OldestInputEvent=9223372036854775807, NewestInputEvent=0, HandleInputStart=2354532725800, AnimationStart=2354532812780, PerformTraversalsStart=2354533033500, DrawStart=2354541099130, SyncQueued=2354542684770, SyncStart=2354683798150, IssueDrawCommandsStart=2354687158970, SwapBuffers=2354843953570, FrameCompleted=2355089974810, DequeueBufferDuration=52711000, QueueBufferDuration=687000, Syncing files to device Android SDK built for x86... I/oper.baby_name( 5789): The ClassLoaderContext is a special shared library. I/chatty ( 5789): uid=10091(com.ruguethedeveloper.baby_names) FirestoreWorker identical 1 line I/oper.baby_name( 5789): The ClassLoaderContext is a special shared library. V/NativeCrypto( 5789): Registering com/google/android/gms/org/conscrypt/NativeCrypto's 284 native methods... W/oper.baby_name( 5789): Accessing hidden method Ljava/security/spec/ECParameterSpec;->getCurveName()Ljava/lang/String; (light greylist, reflection) D/NetworkSecurityConfig( 5789): No Network Security Config specified, using platform default I/ProviderInstaller( 5789): Installed default security provider GmsCore_OpenSSL W/oper.baby_name( 5789): Accessing hidden field Ljava/nio/Buffer;->address:J (light greylist, reflection) W/ManagedChannelImpl( 5789): [{0}] Failed to resolve name. status={1} W/Firestore( 5789): (18.2.0) [OnlineStateTracker]: Could not reach Cloud Firestore backend. Connection failed 1 times. Most recent error: Status{code=UNAVAILABLE, description=Unable to resolve host firestore.googleapis.com, cause=java.lang.RuntimeException: java.net.UnknownHostException: Unable to resolve host "firestore.googleapis.com": No address associated with hostname W/Firestore( 5789): at io.grpc.internal.DnsNameResolver.resolveAll(DnsNameResolver.java:331) W/Firestore( 5789): at io.grpc.internal.DnsNameResolver$1.run(DnsNameResolver.java:214) W/Firestore( 5789): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) W/Firestore( 5789): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) W/Firestore( 5789): at java.lang.Thread.run(Thread.java:764) W/Firestore( 5789): Caused by: java.net.UnknownHostException: Unable to resolve host "firestore.googleapis.com": No address associated with hostname W/Firestore( 5789): at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:157) W/Firestore( 5789): at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:105) W/Firestore( 5789): at java.net.InetAddress.getAllByName(InetAddress.java:1154) W/Firestore( 5789): at io.grpc.internal.DnsNameResolver$JdkAddressResolver.resolveAddress(DnsNameResolver.java:517) W/Firestore( 5789): at io.grpc.internal.DnsNameResolver.resolveAll(DnsNameResolver.java:299) W/Firestore( 5789): ... 4 more W/Firestore( 5789): Caused by: android.system.GaiException: android_getaddrinfo failed: EAI_NODATA (No address associated with hostname) W/Firestore( 5789): at libcore.io.Linux.android_getaddrinfo(Native Method) W/Firestore( 5789): at libcore.io.BlockGuardOs.android_getaddrinfo(BlockGuardOs.java:172) W/Firestore( 5789): at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:137) W/Firestore( 5789): ... 8 more W/Firestore( 5789): } W/Firestore( 5789): This typically indicates that your device does not have a healthy Internet connection at the moment. The client will operate in offline mode until it is able to successfully connect to the backend. D/ ( 5789): HostConnection::get() New Host Connection established 0xee548e00, tid 5814 D/EGL_emulation( 5789): eglMakeCurrent: 0xee545c00: ver 3 0 (tinfo 0xd739def0) D/skia ( 5789): Program linking failed. I/chatty ( 5789): uid=10091(com.ruguethedeveloper.baby_names) 1.gpu identical 7 lines D/skia ( 5789): Program linking failed. W/ManagedChannelImpl( 5789): [{0}] Failed to resolve name. status={1} W/ManagedChannelImpl( 5789): [{0}] Failed to resolve name. status={1} W/ManagedChannelImpl( 5789): [{0}] Failed to resolve name. status={1} W/ManagedChannelImpl( 5789): [{0}] Failed to resolve name. status={1} W/ManagedChannelImpl( 5789): [{0}] Failed to resolve name. status={1} 时使mypy报告警告。

(当然,您总是可以自己动手进行修复!)