实现抽象方法时扩展输入参数,mypy提示‘Signature of "<func_name>" incompatible with supertype "<base_class>" [override]’错误的原因咨询
Let's break down why mypy is complaining and how to fix this issue clearly.
The Root Cause: Liskov Substitution Principle
Your error stems from violating a core object-oriented design rule called the Liskov Substitution Principle (LSP). This principle requires that any subclass should be usable in place of its parent class without breaking the program's logic.
Looking at your code:
- The parent class
Strategiesdefinescompute_signlsto accept zero or more positional arguments (*args: Any). That means code expecting aStrategiesinstance could legally callobj.compute_signls()with no parameters. - But your subclass
MyStrategychanges this to require a mandatory first argumentindicator: pd.DataFrame. Now, if that same code tried to callMyStrategy().compute_signls()(without the indicator), it would crash immediately.
Mypy catches this mismatch because it enforces that subclass method signatures are contravariant—subclass methods can't require more arguments than the parent, only accept fewer or the same set.
Fixes to Try
Option 1: Make the subclass parameter optional (with a default value)
This keeps compatibility with the parent class signature while still allowing your subclass to use the parameter. You'll just need to handle the case where the parameter isn't provided:
class MyStrategy(Strategies): def compute_signls( self, indicator: pd.DataFrame | None = None, *args: Any, **kwargs: Any ) -> dict[str, pd.DataFrame]: if indicator is None: raise ValueError("MyStrategy requires an indicator parameter to compute signals") # Rest of your signal logic here pass
Option 2: Update the parent class to require the parameter
If all strategies you plan to implement need an indicator, then the parent class should enforce this requirement in its abstract method. This makes the signature consistent across parent and child:
class Strategies(ABC): def __init__(self, price: pd.Series | pd.DataFrame) -> None: # Fixed typo: missing parentheses on __init__ self.price = price @abstractmethod def compute_signls(self, indicator: pd.DataFrame, *args: Any, **kwargs: Any) -> dict[str, pd.DataFrame]: """Compute signals"""
(Note: I also fixed a typo in your parent class __init method—you had __init(self, ...) instead of __init__(self, ...))
Option 3: Use a Protocol for stricter subclasses (advanced)
If you need some strategies to work without an indicator and others to require it, you can split this into separate protocols. This lets you enforce different requirements for different strategy types:
from typing import Protocol class BaseStrategy(ABC): @abstractmethod def compute_signls(self, *args: Any, **kwargs: Any) -> dict[str, pd.DataFrame]: pass class IndicatorRequiredStrategy(Protocol): def compute_signls(self, indicator: pd.DataFrame, *args: Any, **kwargs: Any) -> dict[str, pd.DataFrame]: pass class MyStrategy(BaseStrategy, IndicatorRequiredStrategy): def compute_signls( self, indicator: pd.DataFrame, *args: Any, **kwargs: Any ) -> dict[str, pd.DataFrame]: pass
This is more flexible but adds complexity, so only use it if you need to support both types of strategies.
Quick Side Note
You might want to fix the typo in your method name: compute_signls should probably be compute_signals—it'll avoid confusion down the line!
内容的提问来源于stack exchange,提问作者Andi

