簡體   English   中英

如何創建包含 threading.Event 和 multiprocessing.Event 的協議?

[英]How can I create a Protocol that encompases both threading.Event and multiprocessing.Event?

在 Python 標准庫中multiprocessing.Event被明確聲明為threading.Event的克隆並具有相同的接口。 我想注釋變量和參數,以便它們可以接受這些類中的任何一個,並且mypy會對它們進行類型檢查。 我嘗試創建一個協議(我使用multiprocessing.synchronize.Event因為這是multiprocessing.Event返回的實際類)。

import multiprocessing
import threading

from typing import Optional, Type, Protocol


class Event(Protocol):
    def wait(self, timeout: Optional[float]) -> bool:
        ...

    def set(self) -> None:
        ...

    def clear(self) -> None:
        ...

    def is_set(self) -> bool:
        ...


class Base:
    flag_class: Type[Event]

    def foo(self, e: Event):
        pass


class DerivedOne(Base):
    flag_class = multiprocessing.synchronize.Event

    def foo(self, e: multiprocessing.synchronize.Event):
        pass


class DerivedTwo(Base):
    flag_class = threading.Event

    def foo(self, e: threading.Event):
        pass

但是, mypy (0.761 版)無法識別multiprocessing.Eventthreading.Event都實現了我定義的協議:

$ mypy proto.py

proto.py:31: error: Argument 1 of "foo" is incompatible with supertype "Base"; supertype defines the argument type as "Event"
proto.py:38: error: Argument 1 of "foo" is incompatible with supertype "Base"; supertype defines the argument type as "Event"
Found 2 errors in 1 file (checked 1 source file)

為什么mypy不能識別我的協議,我該如何解決?

這不是Protocol問題。 您將foo的簽名更改為更嚴格的變體 Base.foo()接受任何Event實現,而您的每個子類只接受一個具體的實現。 這違反了Liskov 替換原則,Mypy 不允許這樣做是正確的。

您必須在此處使用綁定TypeVarGeneric的組合,以便您可以創建采用不同類型的Base不同具體子類:

from typing import Generic, Optional, Protocol, Type, TypeVar

# Only things that implement Event will do
T = TypeVar("T", bound=Event)

# Base is Generic, subclasses need to state what exact class
# they use for T; as long as it's an Event implementation, that is.
class Base(Generic[T]):
    flag_class: Type[T]

    def foo(self, e: T) -> None:
        pass

這本質上使Base成為一種模板類, T是模板插槽,您可以插入任何東西,只要任何東西都實現了您的協議​​。 這也更加健壯,因為您現在不會意外地混淆Event實現(將threading.Eventmultiprocessing.Event組合在一個子類中)。

因此,以下兩個不同Event實現的子類是正確的:

class DerivedOne(Base[multiprocessing.synchronize.Event]):
    flag_class = multiprocessing.synchronize.Event

    def foo(self, e: multiprocessing.synchronize.Event) -> None:
        pass

class DerivedTwo(Base[threading.Event]):
    flag_class = threading.Event

    def foo(self, e: threading.Event) -> None:
        pass

但是使用一個沒有實現協議方法的類是一個錯誤:

# Mypy flags the following class definition as an error, because a lock
# does not implement the methods of an event.
# error: Type argument "threading.Lock" of "Base" must be a subtype of "proto.Event"
class Wrong(Base[threading.Lock]):
    flag_class = threading.Lock

    def foo(self, e: threading.Lock) -> None:
        pass

混合類型也是錯誤的:

# Mypy flags 'def foo' as an error because the type it accepts differs from
# the declared type of the Base[...] subclass
# error: Argument 1 of "foo" is incompatible with supertype "Base"; supertype defines the argument type as "Event"
class AlsoWrong(Base[threading.Event]):
    flag_class = threading.Event

    def foo(self, e: multiprocessing.synchronize.Event) -> None:
        pass

# Mypy flags 'flag_class' as an error because the type differs from the
# declared type of the Base[...] subclass
# error: Incompatible types in assignment (expression has type "Type[multiprocessing.synchronize.Event]", base class "Base" defined the type as "Type[threading.Event]")
class StillWrong(Base[threading.Event]):
    flag_class = multiprocessing.synchronize.Event

    def foo(self, e: threading.Event) -> None:
        pass

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM