beatbox4108
@beatbox4108

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

PythonでmypyにLSP違反を指摘されないように引数の型を狭めるオーバーライドを行う方法

解決したいこと

Pythonでの型安全性を担保したいと思い、プロジェクトの途中にmypyを導入しました。
ですが、既存のコード内に親クラスより子クラスのメソッドの引数の型が狭まるような書き方をしていた箇所があり、mypyにLSPの違反として指摘されてしまっています。なるべくコードの構成を変えないままリライトしたいと考えていますが、方法がわかりません。

発生している問題・エラー

Argument 1 of "load_source" is incompatible with supertype "Stream"; supertype defines the argument type as "Source"
This violates the Liskov substitution principle
See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides

コードの構造を模倣したソースコード

import abc

class Source(abc.ABC):
    pass

class Stream(abc.ABC):
    def load_source(self,source:Source):
        pass

class SourceA(Source):
    # SourceAはStreamA専用のソースクラス。
    pass

class StreamA(Stream):
    def load_source(self, source: SourceA):
        # StreamAとしてはSourceAのみを扱える。
        pass

自分で試したこと

LSPについて疎かったので軽く調べましたが有用な情報を見つけられませんでした...

0

2Answer

コードの構成を変えたくないのであればエラーを抑制するだけで十分だと思います。

class StreamA(Stream):
    def load_source(self, source: SourceA): # type: ignore[override]
        pass
0Like

一応、load_sourceの引数の型を外部から渡すという方法もあります。ただ、StreamSourceの使い方によっては、このような書き換えはできません。

import abc
from typing import TypeVar, Generic

class Source(abc.ABC):
    pass

T = TypeVar('T', bound=Source)

class Stream(abc.ABC, Generic[T]):
    def load_source(self,source:T):
        pass

class SourceA(Source):
    # SourceAはStreamA専用のソースクラス。
    pass

class StreamA(Stream[SourceA]):
    def load_source(self, source: SourceA):
        # StreamAとしてはSourceAのみを扱える。
        pass

def f(stream: Stream[T], source: T):
    stream.load_source(source)

f(StreamA(), SourceA())
0Like

Your answer might help someone💌