Reputation: 2544
Let's say I have these classes:
class GenericCopyable:
def copy(self) -> GenericCopyable:
... # whatever is required to copy this
class CopyableFoo(GenericCopyable):
... # uses the parent implementation of "copy"
def bar(self): …
def some_code(victim: CopyableFoo):
v = victim.copy()
v.bar() ### I know that this works because "v" is a CopyableFoo, but mypy doesn't
The problem is that I need the return type of CopyableFoo.copy()
to be CopyableFoo
, not GenericCopyable
.
Is that possible?
Edited: The above is sample code to illustrate the problem. Modifying some_code
or CopyableFoo
in some way is certainly possible in this example; in my "real" program that'd be substantially more difficult.
Upvotes: 3
Views: 1423
Reputation: 31
Since Python 3.11, the standard library contains an explicit special type for this - Self.
This replaces the TypeVar
approach suggested in @Axe319's answer - note that the docs referenced above explicitly mention this.
The base class with Self
would be written like this:
from typing import Self
class GenericCopyable:
def copy(self) -> Self:
...
This specifies to type checkers that any instance of GenericCopyable
returns an instance of the same type as itself from its copy()
method.
Upvotes: 2
Reputation: 4365
You could do this.
from typing import TypeVar
# We define T as a TypeVar bound to the base class GenericCopyable
T = TypeVar('T', bound='GenericCopyable')
class GenericCopyable:
# we return the type T of the type of self
# Basically returning an instance of the calling
# type's class
def copy(self: T) -> T:
return type(self)()
class CopyableFoo(GenericCopyable):
pass
foo = CopyableFoo()
bar = foo.copy()
print(bar)
This looks a little clumsy because typically we don't need to annotate self
, since it's implicitly a type of the class it's bound to.
However, mypy seems to be ok with it.
Upvotes: 4
Reputation: 844
One possible solution is to override the method in your children class and then call your superclass method with the children class method specifying the return type of their own instance.
class GenericCopyable:
def copy(self) -> GenericCopyable:
... # whatever is required to copy this
class CopyableFoo(GenericCopyable):
def copy(self)->CopyableFoo:
return super().copy()
Another possible solution is by using the typing module to import Union. This specifies the function in your parent class is able to return multiple types
from typing import Union
class GenericCopyable:
def copy(self) -> Union[GenericCopyable,CopyableFoo]:
... # whatever is required to copy this
class CopyableFoo(GenericCopyable):
#Call parent class method directly
GenericCopyable.copy()
Upvotes: 1