gpt4 book ai didi

python - 暗示类正在实现协议(protocol)的正确方法?

转载 作者:行者123 更新时间:2023-12-05 01:50:27 26 4
gpt4 key购买 nike

在改进我的 Python 开发工作的道路上。我有兴趣在 CI/deb 构建时测试使用 Protocol 定义的接口(interface),这样如果接口(interface)实际上没有被类实现,我们将在单元测试运行后立即知道。

我的方法是使用 Protocol 进行输入并使用 implements runtime_checkable 来构建单元测试。这行得通,但团队就如何指示具体实现协议(protocol)而不破坏 runtime_checkable 展开了一场小辩论。在 C++/Java 中,您需要继承来指示接口(interface)的实现,但在 Python 中,您不一定需要继承。谈话的中心是我们是否应该从协议(protocol)接口(interface)类继承。

在最后考虑这个代码示例,它提供了问题的大部分要点。我们正在考虑 Shape 并指出如何向 future 的开发人员暗示 Shape 正在提供 IShape,但是通过继承这样做会使 isinstance 的 runtime_checkable 版本无法用于其单元测试目的。

这里有几个改进的途径:

我们可以找到一种更好的方式来暗示 Shape 实现了不涉及直接继承的 IShape。我们可以找到一种更好的方法来检查接口(interface)是否在测试 deb 包构建时实现。也许 runtime_checkable 是错误的想法。

有人得到了如何更好地使用 Python 的指导吗?谢谢!


from typing import (
Protocol,
runtime_checkable
)
import dataclasses

@runtime_checkable
class IShape(Protocol):
x: float


@dataclasses.dataclass
class Shape(IShape):
foo:float = 0.

s = Shape()
# evaluates as True but doesnt provide the interface. Undermines the point of the run-time checkable in unit testing
assert isinstance(s, IShape)
print(s.x) # Error. Interface wasnt implemented




#
# Contrast with this assert
#
@dataclasses.dataclass
class Goo():
x:float = 1

@dataclasses.dataclass
class Hoo():
foo: float = 1

g = Goo()
h = Hoo()
assert isinstance(g, IShape) # asserts as true
# but because it has the interface and not because we inherited.
print(g.x)


assert isinstance(h, IShape) # asserts as False which is what we want

最佳答案

在谈论静态类型检查时,有助于理解子类型与子类不同的概念。 (在 Python 中,类型和类是同义词;在 mypy 等工具实现的类型系统中并非如此。)

类型T 是类型S名义 子类型,如果我们明确说明它是的话。子类化是名义子类型化的一种形式:TS 的子类型,如果(但不是如果)TS 的子类。

T 类型是 S 类型的结构子类型,如果它与 T 本身兼容S。协议(protocol)是 Python 的结构子类型化实现。 Shape 不需要是 IShape 的标称子类型(通过子类化)才能成为 IShape 的结构子类型(通过具有x 属性)。

因此,将 IShape 定义为 Protocol 而不仅仅是 Shape 的父类(super class)的意义在于支持结构子类型化并避免需要名义子类型化(以及继承可能引入的所有问题)。

class IShape(Protocol):
x: float


# A structural subtype of IShape
# Not a nominal subtype of IShape
class Shape:
def __init__(self):
self.x = 3

# Not a structural subtype of IShape
class Unshapely:
def __init__(self):
pass


def foo(v: IShape):
pass

foo(Shape()) # OK
foo(Unshapely()) # Not OK

那么结构子类型是否可以替代名义子类型?一点也不。继承有它的用处,但是当它是您的唯一子类型化方法时,它会被不恰本地使用。一旦您在类型系统中区分了结构子类型和名义子类型,您就可以使用适合您实际需要的子类型。

关于python - 暗示类正在实现协议(protocol)的正确方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73069962/

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