質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.34%
型推論

型推論とは、コンパイラが型を自動で判断する機能を指します。メソッド内のローカル変数の宣言時に型宣言の代わりに指定することで、コードの記述量を減らすことが可能。変数や関数シグネチャに型を宣言せずとも、早期にエラーをチェックできるというメリットもあります。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

1回答

181閲覧

mypy で、override したときに起きる Signature incompatible エラーが納得いかない

jmc

総合スコア8

型推論

型推論とは、コンパイラが型を自動で判断する機能を指します。メソッド内のローカル変数の宣言時に型宣言の代わりに指定することで、コードの記述量を減らすことが可能。変数や関数シグネチャに型を宣言せずとも、早期にエラーをチェックできるというメリットもあります。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

1グッド

0クリップ

投稿2025/01/01 12:59

実現したいこと

mypy で、メソッドの戻り値を、(引数によっては)狭めるような override をするスタブファイル test.pyi を作りたい。

発生している問題・分からないこと

test.pyi にて、以下のエラーメッセージが発生しました。

エラーメッセージ

error

1test.pyi:14: error: Signature of "__add__" incompatible with supertype "数式" [override] 2test.pyi:14: note: Superclass: 3test.pyi:14: note: def __add__(self, int | float | 数式, /) -> 数式 4test.pyi:14: note: Subclass: 5test.pyi:14: note: @overload 6test.pyi:14: note: def __add__(self, int | float | 実数, /) -> 実数 7test.pyi:14: note: @overload 8test.pyi:14: note: def __add__(self, 数式, /) -> 数式 9Found 1 error in 1 file (checked 1 source file)

該当のソースコード

python:test.pyi

1from typing import overload 2 3class 数式: 4 def __add__(self, other:int|float|数式)->数式: 5 # 例0: 数式("x") + 数式("y") = 数式("x+y") 6 # 例1: 数式("x") + 実数("1") = 数式("x+1") 7 # 例2: 数式("x") + 1 = 数式("x+1") 8 # 例3: 実数("1") + 数式("x") = 数式("x+1") 9 # 例4: 実数("1") + 実数("2") = 実数("3") 10 # 例5: 実数("1") + 2 = 実数("3") 11 pass 12 13class 実数(数式): 14 @overload 15 def __add__(self, other:int|float|実数)->実数: 16 # 例4: 実数("1") + 実数("2") = 実数("3") 17 # 例5: 実数("1") + 2 = 実数("3") 18 pass 19 @overload 20 def __add__(self, other:数式)->数式: 21 # 例3: 実数("1") + 数式("x") = 数式("x+1") 22 # 例4: 実数("1") + 実数("2") = 実数("3") 23 pass

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果
「teratailやGoogle等で検索した」 について

Google で Signature of method incompatible with supertypeを検索したところ、
こちら の Qiita の記事を見つけました。

そこで、 リスコフの置換原則 (LSP) という言葉が出てきたので、Google でLSPについて調べました。

LSPについて分かったことを自分なりの言葉で表現すると、「親に出来ることは、子にも出来ろ」ということです。

  • 子クラスではメソッドの引数を親より狭めてはならない
    (例えば 親メソッドが int|str を入力できるのに、子メソッドが int しか入力できないよう引数を狭めてしまうと、「親に出来た str の入力が、子には出来ない」という事態が発生し、LSPに違反します)
  • 子クラスではメソッドの戻り値を親より広げてはならない
    (例えば 親メソッドが int を出力するのに、子メソッドが int|str を出力するよう戻り値を広げてしまうと、「親に出来た "出力を2で割る演算" ((int)/2) が、子には出来ない場合がある ((str)/2 はエラーになる)」という事態が発生し、LSPに違反します)

一方、私のコードにおける 子クラスのメソッド ( 実数.__add__ ) では、

  • 引数は int|float|実数, 戻り値は 実数
  • 引数は 数式, 戻り値は 数式

が overload になっており、

  • 可能な引数をまとめると、int|float|数式
  • 可能な戻り値をまとめると 数式

になっています。

これは、親クラスのメソッド ( 数式.__add__ )のシグネチャ

  • 引数は int|float|数式, 戻り値は 数式

と一致しているため、どこが Signature of "__add__" incompatible with supertype "数式" なのかわからないのです。

また、入出力を表にまとめると、次の通りです。

入力\出力親メソッド子メソッド子の出力は親より
int|float|実数数式実数狭い
実数以外の数式数式数式同じ

この表から、特定の型の入力があった場合に出力が親より狭まることはあっても広げることはない。(LSPに違反する入力パターンはない)ということがないことがわかります。

「ソースコードを自分なりに変更した」 について

test.pyiの20行目を def __add__(self, other:int|float|数式)->数式: に変更したところ、 mypy がエラーを返すことは無くなりましたが、可読性の観点からこのような変更は極力避けたいです。

補足

環境は python 3.13.1, mypy 1.14.0 を利用しています。

また、本当のことをいうと、本件の問題は、sympy 1.13.3 の スタブファイルを(自分にとって必要な箇所だけ)自作しているときに直面しました。具体的には、

python:sympy.pyi

1from typing import overload 2 3class Expr: # sympyの式 4 def evalf(self, n:int)->Float: pass 5 def __truediv__(self, other:int|float|Expr)->Expr: pass 6 7class Float(Expr): # sympyの実数。sympyの式 の一種 8 @overload 9 def __truediv__(self, other:int|float|Float)->Float: pass 10 @overload 11 def __truediv__(self, other:Expr)->Expr: pass

のようなスタブファイルを作っていた時に、

error

1sympy.pyi:8: error: Signature of "__truediv__" incompatible with supertype "Expr" [override] 2sympy.pyi:8: note: Superclass: 3sympy.pyi:8: note: def __truediv__(self, int | float | Expr, /) -> Expr 4sympy.pyi:8: note: Subclass: 5sympy.pyi:8: note: @overload 6sympy.pyi:8: note: def __truediv__(self, int | float | Float, /) -> Float 7sympy.pyi:8: note: @overload 8sympy.pyi:8: note: def __truediv__(self, Expr, /) -> Expr 9Found 1 error in 1 file (checked 1 source file)

というエラーに直面したのですが、sympy に詳しくない方が回答する気を失ったりしたらもったいないので、敢えて sympy が関係ないコードを別に用意し、質問させていただきました。

なので、もしも python 3.13.1, mypy 1.14.0 sympy 1.13.3 で正常に動作する sympyのスタブファイルをお持ちか、 sympy に型ヒントを追加する方法をご存じの方がいらっしゃいましたら、そちらも回答いただけますと大変ありがたいです。

以上、何卒宜しくお願い致します。

melian👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

stackoverflow で海外のエンジニアの方に質問したところ、これは既知の問題であるが、すぐに解決することを期待できないとのコメントをいただきました。
(例えば、mypyとは別の静的型チェッカ pyright などでは、overload を使って override されたメソッドは、不整合とみなされる (No overload signature in override is compatible with base method) ようで、 mypyでも同じことなのかもしれません。)

(2025/1/5追記) かつて提案した下記の方法は、 --warn-unused-ignores フラグをつけて mypy を利用した場合にエラーとなるので、おすすめされません。

また、根本的な解決ではありませんが、次のようにすると、エラーを隠せることに気が付きました。

python:test.pyi(エラーを隠す)

1from typing import overload 2引用テキスト 3class 数式: 4 def __add__(self, other:int|float|数式)->数式: 5 # 例0: 数式("x") + 数式("y") = 数式("x+y") 6 # 例1: 数式("x") + 実数("1") = 数式("x+1") 7 # 例2: 数式("x") + 1 = 数式("x+1") 8 # 例3: 実数("1") + 数式("x") = 数式("x+1") 9 # 例4: 実数("1") + 実数("2") = 実数("3") 10 # 例5: 実数("1") + 2 = 実数("3") 11 pass 12引用テキスト 13class 実数(数式): 14 @overload 15 def __add__(self, other:int|float|実数)->実数: 16 # 例4: 実数("1") + 実数("2") = 実数("3") 17 # 例5: 実数("1") + 2 = 実数("3") 18 pass 19 @overload 20 def __add__(self, other:数式)->数式: 21 # 例3: 実数("1") + 数式("x") = 数式("x+1") 22 # 例4: 実数("1") + 実数("2") = 実数("3") 23 pass 24 # エラー隠し ここから 25 @overload 26 def __add__( # type: ignore[overload-cannot-match] 27 self, other:int|float|数式)->数式: 28 # エラー隠し ここまで

最後の overload に、親と全く同じシグネチャのメソッドを追加することで、強制的に整合させました。
但し、このやり方では、将来のバージョンにて、本記事で議論している問題が解決されたときに、メソッドの追加部分にコードが到達しなくなるという問題が発生します。
mypy ではこれは [overload-cannot-match] というエラーになりますので、将来のバージョンに備えて予め # type: ignore[overload-cannot-match] しています。
引用テキスト
(一見すると、単に # type: ignore でエラーを黙らせただけに見えるかもしれませんが、ここで追加した # type: ignore[overload-cannot-match] はあくまで将来発生するエラーに対するものであり、現行の mypy とは無関係です。)

投稿2025/01/02 01:52

編集2025/01/05 05:48
jmc

総合スコア8

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.34%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問