微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

结构鸭子类型是否与 LSP 兼容?

如何解决结构鸭子类型是否与 LSP 兼容?

在某些情况下,结构子类型是否会导致无法使用 LSP?例如,假设我有一个 Python* 设置,其标称类型为

class Foo(ABC):
    def frobnicate(self) -> int:
        """ frobnicate in a general fashion """

class SubFoo(Foo):
    def frobnicate(self) -> int:
        """ frobnicate in a particular fashion """

def bar(f: SubFoo) -> int:
    return f.frobnicate()

我可以在 bar 中确定我有一个 SubFoo 并且由于 LSP 我知道调用它会“以特定的方式frobnicate”。这一切都很好。现在假设我改用结构子类型(注意 Protocol

class Foo(Protocol):
    def frobnicate(self) -> int:
        """ frobnicate in a general fashion """

class SubFoo(Foo,Protocol):
    def frobnicate(self) -> int:
        """ frobnicate in a particular fashion """

def bar(f: SubFoo) -> int:
    return f.frobnicate()

我不能确定在 bar 我有一个 SubFoo 因为一个 Foo 也满足那个协议,所以我可能,天堂禁止,“以一般方式frobnicate” .也就是说,LSP 不适用,因为子类型和超类型之间没有区别。

*我以 Python 为例,因为这就是我所知道的。这是一个语言不可知的问题,但我意识到这可能是由于 Python 的细节

解决方法

LSP 是一致性声明,而不是实现。

子类型要求:令 ϕ(x) 是一个关于类型 T 的对象 x 的可证明属性。那么对于类型 S 的对象 y,ϕ(y) 应该为真,其中 S 是 T 的子类型。

请注意,LSP 仅涉及一般的类型和子类型,而不是名义结构或其他特定的子类型。

转述,可以说“无论类型保证什么,它的子类型也必须保证”。由于结构类型与结构有关 – 表示为类型保证 – 这一点很容易满足。


结构类型根据结构定义子类型。例如,Foo 定义了结构类型:

{frobnicate: (Self) -> int}

值得注意的是,这表示 frobnicate 存在,采用 Foo 并返回 int。它没有说明这是如何发生的,或者输入和输出之间有什么关系。
重要的是,Foo 并未以一般或特定方式说明是 frobnicate实现、文档字符串或类似内容不是类型的一部分。

同样,SubFoo 定义了完全相同的结构类型:

{frobnicate: (Self) -> int}

就 LSP 而言,替换 SubFoo <: FooFoo <: SubFoo 都是有效的:FooSubFoo 仅在捕获方式上有所不同通过结构类型,因此不能由任何一种类型保证。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。