Embora a assinatura do método em Sub
seja compatível com Super
, mypy rejeita a substituição: Signature of "method" incompatible with supertype "Super"
.
estou usando
- python 3.13.1
- meupy 1.14.0
Primeiro, fiz o seguinte test.pyi
.
from typing import overload
class Super:
def method(self, arg:Other|Super)->Super: pass
class Sub(Super):
@overload
def method(self, arg:Other|Sub)->Sub: pass
@overload
def method(self, arg:Super)->Super: pass
class Other: pass
Então, quando executei mypy test.pyi
na linha de comando, o mypy produziu o seguinte diagnóstico:
test.pyi:7: error: Signature of "method" incompatible with supertype "Super" [override]
test.pyi:7: note: Superclass:
test.pyi:7: note: def method(self, arg: Other | Super) -> Super
test.pyi:7: note: Subclass:
test.pyi:7: note: @overload
test.pyi:7: note: def method(self, arg: Other | Sub) -> Sub
test.pyi:7: note: @overload
test.pyi:7: note: def method(self, arg: Super) -> Super
Found 1 error in 1 file (checked 1 source file)
Verifiquei o tipo de E/S de ambos Super.method
e Sub.method
e descobri que não há nenhum padrão que viole o LSP (Princípio de Substituição de Liskov).
Sub.method
Lata sobrecarregada
- entrada
arg
do tipoOther|Super
(=Other|Sub
+Super
) e - tipo de saída
Super
(=Sub
+Super
).
O tipo de entrada e saída acima corresponde à assinatura de Super.method
.
Então, não tenho ideia do que ser Signature of "method" incompatible with supertype "Super"
.
A seguir está a tabela de E/S de method
.
Eu\O | Super.method |
Sub.method |
Comparado a Super , Sub o retorno de é: |
Aderindo ao LSP* |
---|---|---|---|---|
Other |
Super |
Sub |
mais estreito | Sim |
Sub |
Super |
Sub |
mais estreito | Sim |
Super |
Super |
Super |
o mesmo | Sim |
Other|Sub |
Super |
Sub |
mais estreito | Sim |
Other|Super |
Super |
Super ** |
o mesmo | Sim |
*O LSP exige que o tipo de retorno de um submétodo seja mais estreito ou igual ao tipo de retorno do supermétodo.
** Sub.method
retorna
Sub
quandoOther
a entrada eSuper
quandoSuper
a entrada,
então ele retorna Sub|Super
quando Other|Super
a entrada.
Sub|Super
significa Super
.
Como você pode ver na tabela acima, não há padrões que violem o LSP.
Então, acho que a mensagem de erro mypy Signature of "method" incompatible with supertype "Super"
está incorreta.
Meu código está errado?
Além disso, se meu código não estiver errado e a mensagem de erro do mypy estiver errada, onde posso perguntar?
PS Uma maneira rápida de esconder o erro.
Embora não seja uma solução completa, encontrei uma maneira simples de esconder o erro.
from typing import overload
class Super:
def method(self, arg:Other|Super)->Super: pass
class Sub(Super):
@overload
def method(self, arg:Other|Sub)->Sub: pass
@overload
def method(self, arg:Super)->Super: pass
# Start of hiding error
@overload
def method( # type: ignore[overload-cannot-match]
self, arg:Other|Super)->Super: pass
# End of hiding error
class Other: pass
Como uma última sobrecarga, adicionei um Sub.method
com a mesma assinatura exata de Super.method
. No entanto, quando esse problema for resolvido em uma versão futura, isso significará que o código que adicionei não será alcançado e devemos obter um [overload-cannot-match]
erro. Portanto, adicionei # type: ignore[overload-cannot-match]
para ignorar esse erro com antecedência.
(À primeira vista, pode parecer que estou simplesmente silenciando erros com type: ignore
, mas isso não é relevante para o mypy no momento. Isso é apenas um impedimento contra erros futuros.)
Conforme apontado em um tíquete de direitos autorais sobre isso , a especificação de digitação menciona esse comportamento explicitamente:
Há um ticket mypy perguntando sobre o mesmo problema.
No entanto, seu código é de fato seguro, e a tabela em seu post prova isso. Isso é apenas uma limitação da especificação e/ou dos typecheckers.
Conforme discutido nos comentários, pode parecer que o problema é o próprio tipo de união (
Other | Super
não pode ser despachado para nenhuma das sobrecargas), mas não é verdade: mypy usa matemática de união nesse caso, e o tipo de retorno de chamada sobrecarregado é uma união de tipos de retorno de sobrecargas correspondentes se todos os membros da união puderem ser despachados para um deles. Aqui está a fonte onde essa mágica acontece, leia os comentários lá se estiver interessado - o caminho do verificador principal está bem documentado.Agora, dado que seu código não passa pela verificação de tipo apenas devido a um problema de verificação de tipo/especificação, você tem várias opções:
# type: ignore[override]
- por que não? Seu override está seguro, apenas diga ao mypy para calar a boca.Adicione uma assinatura para corresponder aos requisitos de especificação como no seu último parágrafo. Mas, por favor, não adicione um comentário ignore não utilizado - há um
--warn-unused-ignores
sinalizador paramypy
o qual é realmente útil. Não adicione tal ignore, apenas adicione um comentário de texto livre explicando o problema e vinculando aqui ou ao problema mypy.Apenas estenda a segunda assinatura. Isso é seguro - sobrecargas são tentadas em ordem, a primeira correspondência vence (bem, não exatamente, mas em casos simples sem *args/**kwargs e ParamSpec isso é verdade):
Mas eu simplesmente ignoraria o comentário e explicaria o problema: