3

Python 3.7+: I'm trying to define an ABC with an abstract method:

from abc import ABC, abstractmethod

class A(ABC):
    @abstractmethod
    def f(self, x, **kwargs):
        pass

I would like to define, in subclasses, the specific parameters each subclass supports explicitly:

class B(A):
    def f(self, x, y):
        print(x, y)

B().f("hello", "world") seems to work, but PyCharm complains with Signature of method 'B.f()' does not match signature of the base method in class 'A' and I haven't dared to test it with flake or mypy.

Am I abusing the language? Is there a better way to do it, apart from passing **kwargs unchanged? I wanted something defined in a more fixed, explicit way, like standard parameters and :param: in documentation, instead of describing to the users the parameters as strings and looking them up through string from **kwargs.

ragazzojp
  • 477
  • 3
  • 14
  • Can you clarify *why* you use ``**kwargs``? The code as shown indiciates that for any ``a: A`` it is valid to call e.g. ``a.f(x=1, y=2, z=3)``, which is *not* valid for instances of ``B``. – MisterMiyagi May 21 '21 at 11:14
  • I use `**kwargs` because the exact parameters are subclass-specific, and this should stay. What I'm trying to avoid is using `**kwargs` in `B`. – ragazzojp May 21 '21 at 11:40
  • It's hard to tell what you should do, since the example doesn't really capture any intentions. It's okay to define such classes, and often it's perfectly fine from a *practical* point of view. But be aware that the *meaning* of subclassing (as in [LSP](https://en.wikipedia.org/wiki/Liskov_substitution_principle)) is violated by this, so there are some cases where this will produce weird/wrong behaviour – whether you practically hit/need these cases is the question. – MisterMiyagi May 21 '21 at 11:46
  • 1
    More concretely, "the exact parameters are subclass-specific" doesn't make much sense for an ABC – the entire point of the ABC is to define what methods *including their parameters* are expected. When users have to look up what each specific implementation needs, the ABC is meaningless. If you just want to collect some similar types into one, a ``typing.Union`` would be more appropriate (and a supported runtime "thing" in Python 3.10+). – MisterMiyagi May 21 '21 at 11:52
  • 1
    The example is oversimplified, let's not question too much _why_ and let's try to focus on the question. `A.f` has some parameters that are common across the hierarchy (`x` in the example) and some others that are less strict and subclass specific, optional, to further tune the behavior of `f`. And that's fine in Python. It would be perfectly fine to pass `**kwargs` unchanged. I'm just asking if there's a better way to model the subclass-specific, additional parameters in the signature of `B.f`. – ragazzojp May 21 '21 at 12:06
  • If the additional parameters are optional, just removing the ``**kwargs`` from the ABC should work. – MisterMiyagi May 21 '21 at 12:14
  • PyCharm shows the same warning, and users would have no clue that functions may support subclass-specific additional arguments. Probably, the only way is passing `**kwargs` unchanged. Let's wait if someone has a better idea. – ragazzojp May 21 '21 at 12:19
  • That's weird. Defining ``def f(self, x, y=12):`` in ``B`` (i.e. optional additional parameters) makes my PyCharm happy – with and without the ``**kwargs`` in ``A.f``. – MisterMiyagi May 21 '21 at 12:21

0 Answers0