Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Typing on child class' methods of a Generic base class

258 views
Skip to first unread message

Nicolas Haller

unread,
Mar 9, 2022, 12:26:17 PM3/9/22
to
Hello,

I am wondering about a situation involving typing, Generic and a child
class.

The documentation about "user-defined generic types"[1] says that I can
fix some types on a child class (class MyDict(Mapping[str, T]):) but
doesn't say much about the signature of the methods I need to
implement/override on that child class.

Should I keep the TypeVar and remember that I fixed the type(alternative
1) or change the signature of the method to replace the TypeVar by the
type I set (alternative 2)?

Mypy seems to prefer the second alternative but changing the signature
feels wrong to me for some reason. That's why I'm asking :-)


Below is a small example to illustrate:
----
from abc import ABCMeta, abstractmethod
from typing import TypeVar, Generic


T = TypeVar("T")


class Base(Generic[T], metaclass=ABCMeta):
"""A base class."""

@abstractmethod
def _private_method(self, an_arg: T) -> T:
...

def public_method(self, an_arg: T) -> T:
from_private_method = self._private_method(an_arg)
return from_private_method


class Alternative1(Base[int]):
def _private_method(self, an_arg: T) -> T: # I keep T
return 42


class Alternative2(Base[int]):
def _private_method(self, an_arg: int) -> int: # I replace T
return 42
----

Thanks,

--
Nicolas Haller


[1]:
https://docs.python.org/3/library/typing.html#user-defined-generic-types

Julio Di Egidio

unread,
Mar 9, 2022, 1:18:32 PM3/9/22
to
Besides that there is no T in that context, do use int there,
you are "fixing" the type parameter.

BTW, "protected" would be more apt in that case...

HTH,

Julio

Dieter Maurer

unread,
Mar 10, 2022, 12:32:33 PM3/10/22
to
Nicolas Haller wrote at 2022-3-9 10:53 -0500:
> ...
>The documentation about "user-defined generic types"[1] says that I can
>fix some types on a child class (class MyDict(Mapping[str, T]):) but
>doesn't say much about the signature of the methods I need to
>implement/override on that child class.

I have the fealing that this is a case of (generic type) "specialization".
In this setup, `Mapping` would be a generic type with two type
variables `K` (the key type) and `V` (the value type).
The signatures of its methods would use those type variables.
In your example, you specialize the key type to `str` (and
leave the value type generic). The signatures of the methods
would automatically follow this specialization -- without the need
to do anything.

Julio Di Egidio

unread,
Mar 10, 2022, 5:51:10 PM3/10/22
to
On Thursday, 10 March 2022 at 18:32:33 UTC+1, Dieter Maurer wrote:

> The signatures of the methods [in the derived class]
> would automatically follow this specialization -- without the need
> to do anything.

That depends entirely on the tools, typing in Python remains just
annotations. And I wouldn't do that anyway: the code in the derived
class without explicit annotations becomes simply opaque to
any typing (you just see it if you have intellisense or similar), and
readability is the most important and here almost the unique thing...

Julio

Nicolas Haller

unread,
Mar 12, 2022, 12:07:01 PM3/12/22
to
On 2022-03-10 12:31, Dieter Maurer wrote:
> Nicolas Haller wrote at 2022-3-9 10:53 -0500:
>> ...
>> The documentation about "user-defined generic types"[1] says that I can
>> fix some types on a child class (class MyDict(Mapping[str, T]):) but
>> doesn't say much about the signature of the methods I need to
>> implement/override on that child class.
>
> I have the fealing that this is a case of (generic type) "specialization".
> In this setup, `Mapping` would be a generic type with two type
> variables `K` (the key type) and `V` (the value type).
> The signatures of its methods would use those type variables.
> In your example, you specialize the key type to `str` (and
> leave the value type generic). The signatures of the methods
> would automatically follow this specialization -- without the need
> to do anything.

If I understand correctly, you're saying I should use the first
alternative by keeping the signature as it is in the base class:
---
T = TypeVar("T")


class Base(Generic[T], metaclass=ABCMeta):
"""A base class."""

@abstractmethod
def _private_method(self, an_arg: T) -> T:
...

def public_method(self, an_arg: T) -> T:
from_private_method = self._private_method(an_arg)
return from_private_method

class Alternative1(Base[int]):
def _private_method(self, an_arg: T) -> T: # I keep T
return 42
---

The problem with it is that mypy doesn´t seem quite happy with it:
./scratch.py:22: error: Incompatible return value type (got "int",
expected "T")

Do you think this is error is incorrect? If so, I can open an issue with
mypy.

Thanks,

--
Nicolas

Dieter Maurer

unread,
Mar 12, 2022, 1:28:40 PM3/12/22
to
No, I do not think so.

The `Base[int]` means that the generic type variable `T` was
specialized to `int`. This means that in this context
`_private_method` returns `int` (not a generic `T`).

You either do not type annotate the overriding methods
or you use the specialization (if any).

Julio Di Egidio

unread,
Mar 12, 2022, 3:31:23 PM3/12/22
to
P.S. Thanks whoever banned be from the Python list distribution and
without even telling me: very well done, another conquest for humanity.

*Plonk*

Julio
0 new messages