gpt4 book ai didi

python - 如何创建包含 threading.Event 和 multiprocessing.Event 的协议(protocol)?

转载 作者:行者123 更新时间:2023-12-05 02:57:20 26 4
gpt4 key购买 nike

在 Python 标准库中,multiprocessing.Event 被明确声明为 threading.Event 的克隆,并具有相同的接口(interface)。我想注释变量和参数,以便它们可以接受这些类中的任何一个,mypy 将对它们进行类型检查。我尝试创建一个协议(protocol)(我使用了 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 都实现了我定义的协议(protocol):

$ 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),我该如何解决?

最佳答案

这不是协议(protocol)问题。您将 foo 的签名更改为更严格的变体Base.foo() 接受任何 Event 实现,而您的每个子类只接受一个具体实现。这违反了 Liskov substitution principle Mypy 不允许这样做是正确的。

你必须在这里使用绑定(bind)的 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 是您可以插入任何东西的模板槽,只要 任何东西实现你的协议(protocol)。这也更加健壮,因为您现在不会不小心混淆 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

但是使用一个没有实现协议(protocol)方法的类是错误的:

# 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

关于python - 如何创建包含 threading.Event 和 multiprocessing.Event 的协议(protocol)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59774394/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com