[英]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.Event
和threading.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 不允許這樣做是正確的。
您必須在此處使用綁定TypeVar
和Generic
的組合,以便您可以創建采用不同類型的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.Event
和multiprocessing.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.