classification
Title: @overload-ing method of parent class without actual implementation
Type: enhancement Stage:
Components: Versions: Python 3.10
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: chaim422, gvanrossum, uriyyo
Priority: normal Keywords:

Created on 2021-01-03 05:51 by chaim422, last changed 2021-01-12 00:32 by gvanrossum.

Messages (12)
msg384255 - (view) Author: Chaim Gewirtz (chaim422) Date: 2021-01-03 05:51
Why should @overload need to be followed by an implementation when an implementation already exists in the parent class?

Illustrative example:

class Parent:
    def foo(**kwargs):
        """Argument names of foo vary depending on the child class."""

class Child(Parent):
    @overload foo(a, b): ...
    
Raises:

"NotImplementedError: You should not call an overloaded function. A series of @overload-decorated functions outside a stub module should always be followed by an implementation that is not @overload-ed."
msg384265 - (view) Author: Yurii Karabas (uriyyo) * Date: 2021-01-03 11:53
The purpose of `@overload` is quite different. I believe you thought that this is smth like `@override` in Java world but it different.

Basically, the correct usage of `@overaload` is:
```
@overload
def process(response: None) -> None:
    ...
@overload
def process(response: int) -> tuple[int, str]:
    ...
@overload
def process(response: bytes) -> str:
    ...
def process(response):
    <actual implementation>
```

Please, follow this link for more information https://docs.python.org/3/library/typing.html#typing.overload
msg384283 - (view) Author: Chaim Gewirtz (chaim422) Date: 2021-01-03 16:33
"The purpose of `@overload` is quite different." So, this would overload the @overload decorator. Hmmm...

Is there a better way to accomplish this goal? What would you suggest, a new decorator?
msg384284 - (view) Author: Chaim Gewirtz (chaim422) Date: 2021-01-03 16:40
To clarify, this is how it's being done now a dozen times in actual production code:

class Child(Parent):
    @overload foo(a, b): ...
    def overload(**kwargs):
        return super().foo(**kwargs)

The goal of this proposed enhancement is to remove two extra lines of code per Child class.
msg384285 - (view) Author: Yurii Karabas (uriyyo) * Date: 2021-01-03 17:18
`mypy` will produce an error on such code:

```
class Parent:
    def foo(self, **kwargs):
        """Argument names of foo vary depending on the child class."""


class Child(Parent):
    @overload
    def foo(self, a, b):
        pass

    def foo(self, **kwargs):
        return super().foo(**kwargs)
```

Result
```
temp.py:10: error: Single overload definition, multiple required
temp.py:10: error: Signature of "foo" incompatible with supertype "Parent"
Found 2 errors in 1 file (checked 1 source file)
```

In case if you want to add an overload for method I would recommend to use such pattern:
```
class Parent:
    def foo(self, **kwargs):
        """Argument names of foo vary depending on the child class."""


class Child(Parent):
    @overload
    def foo(self, a, b):
        pass

    @overload
    def foo(self, **kwargs):
        pass

    def foo(self, **kwargs):
        return super().foo(**kwargs)
```

So signature of `foo` will still match to parent class signature, but it will also have an overloaded variant.
msg384286 - (view) Author: Chaim Gewirtz (chaim422) Date: 2021-01-03 17:25
Interesting. PyCharm has no problem with this code. It also autocompletes the argument names for me, which is very useful, especially since there a dozen different Child classes.

Isn't "Simple is better than complex", and doesn't "...practicality beat purity"?
msg384287 - (view) Author: Yurii Karabas (uriyyo) * Date: 2021-01-03 17:31
I think the simplest solution in your case is not to use `@overload`, as far as I understand you want to override the signature of base method.

This code won't produce any error when used with `mypy`:
```
class Parent:
    def foo(**kwargs):
        """Argument names of foo vary depending on the child class."""

class Child(Parent):
    def foo(self, a, b):
        pass
``
msg384288 - (view) Author: Chaim Gewirtz (chaim422) Date: 2021-01-03 17:43
What is better?

A. Keeping Python as is, with four separate signature declarations (1x Parent, 2x @overload Child, and 1x actual signature in Child), and a second method call overhead at runtime (to satisfy mypy as it exists now).

--or--

B. Simplify Python to allow just two signature declarations and no extra overhead? 

--or--

C. Something else? Please specify.
msg384289 - (view) Author: Chaim Gewirtz (chaim422) Date: 2021-01-03 17:47
In your example, does Child foo call Parent foo? Because the intent is to use the parent's foo method.
msg384290 - (view) Author: Yurii Karabas (uriyyo) * Date: 2021-01-03 17:55
> What is better?

Sorry, I can't answer this question.

I am a regular python user and I just tried to help with your issue.

I believe we should wait for someone from core team to answer this question.


> In your example, does Child foo call Parent foo? Because the intent is to use the parent's foo method.

Sorry, I made a mistake, it definitely should call a parent method. A correct example will look like this:
```
class Parent:
    def foo(self, **kwargs):
        """Argument names of foo vary depending on the child class."""


class Child(Parent):
    def foo(self, a, b):
        super().foo(a=a, b=b)
```

But, the example above require more code to write, so a better option will be:
```
class Parent:
    def foo(self, **kwargs):
        """Argument names of foo vary depending on the child class."""


class Child(Parent):
    @overload
    def foo(self, a, b):
        pass

    @overload
    def foo(self, **kwargs):
        pass

    def foo(self, **kwargs):
        return super().foo(**kwargs)
```

I am not sure is it the perfect solution to solve your issue.

Let's wait for someone from core team, so we can hear their opinion.
msg384292 - (view) Author: Chaim Gewirtz (chaim422) Date: 2021-01-03 18:26
Thanks for your perspective.

To summarize here is how my proposed enhancement might look in practice:

class Parent:
    def foo(self, **kwargs):
        """Argument names of foo vary depending on the child class."""


class Child(Parent):
    @overload
    def foo(self, a, b):
        pass
msg384871 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2021-01-12 00:32
I hesitate to add anything because you are exposing so much confusion. May I suggest that you ask about this on a user group first before proposing a new feature? One place that makes sense given that this is a type system feature would be this Gitter channel: https://gitter.im/python/typing
History
Date User Action Args
2021-01-12 00:32:35gvanrossumsetnosy: - levkivskyi
messages: + msg384871
2021-01-08 21:40:09terry.reedysetnosy: + gvanrossum, levkivskyi
2021-01-03 18:26:27chaim422setmessages: + msg384292
2021-01-03 17:55:40uriyyosetmessages: + msg384290
2021-01-03 17:47:35chaim422setmessages: + msg384289
2021-01-03 17:43:20chaim422setmessages: + msg384288
2021-01-03 17:31:20uriyyosetmessages: + msg384287
2021-01-03 17:25:06chaim422setmessages: + msg384286
2021-01-03 17:18:04uriyyosetmessages: + msg384285
2021-01-03 16:40:47chaim422setmessages: + msg384284
2021-01-03 16:33:16chaim422setmessages: + msg384283
2021-01-03 11:53:09uriyyosetnosy: + uriyyo
messages: + msg384265
2021-01-03 05:51:32chaim422create