Reputation: 16831
This is what I want to do:
from typing import TypeVar, Generic
T = TypeVar('T')
class Parent:
def __init__(self) -> None:
print("Parent's constructor called")
self.value = 2
def get(self):
return self.value
class Child(T):
def __init__(self):
print("Child's constructor called")
super().__init__()
ch = Child[Parent]()
print(ch.get())
But it gives me an error:
Traceback (most recent call last): File "/tmp/inherit.py", line 13, in class Child(T): File "/home/.../.pyenv/versions/3.9.13/lib/python3.9/typing.py", line 640, in __init__ self.__constraints__ = tuple(_type_check(t, msg) for t in constraints) File "/home/.../.pyenv/versions/3.9.13/lib/python3.9/typing.py", line 640, in self.__constraints__ = tuple(_type_check(t, msg) for t in constraints) File "/home/.../.pyenv/versions/3.9.13/lib/python3.9/typing.py", line 166, in _type_check raise TypeError(f"{msg} Got {arg!r:.100}.") TypeError: TypeVar(name, constraint, ...): constraints must be types. Got (~T,).
Apparently, this is the right way of writing that code:
from typing import TypeVar, Generic
T = TypeVar('T')
class Parent:
def __init__(self) -> None:
print("Parent's constructor called")
self.value = 2
def get(self):
return self.value
class Child(Generic[T]): # <-- "Generic" was added here
def __init__(self):
print("Child's constructor called")
super().__init__()
ch = Child[Parent]()
print(ch.get())
But this one also errors out:
Child's constructor called Traceback (most recent call last): File "/tmp/inherit.py", line 20, in print(ch.get()) AttributeError: 'Child' object has no attribute 'get'
Which is totally expected. Now my question is whether it is possible to extend a generic type in Python at all or not? If it is, how?
Upvotes: 3
Views: 3826
Reputation: 17526
Python has multiple inheritance for this purpose.
class Child(Parent, Generic[T]):
The Generic
is only a type-hint, it doesn't affect the interpreter behaviour, you still have to inherit Parent.
Edit: there's also composition
class Child(Generic[T]):
def __init__(self, parent: T):
print("Child's constructor called")
self.parent: T = parent
def get(self):
return self.parent.get()
ch = Child[Parent](Parent())
print(ch.get())
Edit2: you can override the __getattr__
method to enable child to reuse parent functions in composition, but you cannot type-hint this easily, and it won't be pythonic.
class Child(Generic[T]):
def __init__(self, parent: T):
print("Child's constructor called")
self.parent: T = parent
def __getattr__(self, item: str):
try:
return super().__getattr__(item)
except AttributeError:
return getattr(self.parent, item)
ch = Child[Parent](Parent())
print(ch.get())
Edit3: as mentioned by @deceze you can also create new classes on the fly using type
but the type-hints will need some work.
ch = type('Child', (ChildClass, ParentClass), {})()
usage as docs:
from typing import TypeVar, Generic
T = TypeVar('T')
class Parent:
def __init__(self) -> None:
print("Parent's constructor called")
self.value = 2
def get(self):
print("Parent's get called")
return self.value
class Child:
def __init__(self):
print("Child's constructor called")
super().__init__()
def set(self, v):
print("Child's set called: ", v)
self.value = v
ch = type('Child', (Child, Parent), {})()
print(ch.get())
ch.set(3)
print(ch.get())
which outputs (as expected):
Child's constructor called Parent's constructor called Parent's get called 2 Child's set called: 3 Parent's get called 3
Upvotes: 2