Reputation: 27012
I'm writing a generic class over AnyStr
, so allowing bytes
or str
.
class MyObject(Generic[AnyStr]):
...
Inside (multiple) methods of this class, I would like to construct the empty bytes or empty string object, b''
or ''
, depending on the type parameter. How can I do this?
Upvotes: 1
Views: 988
Reputation: 5887
You should have a base class with the shared methods applying to both str
and bytes
that take advantage of common behavior (for example, both str
and bytes
having length, or both str
and bytes
being indexable), and two subclasses providing implementations for the specific behaviors. To force the subclasses to provide those specific behaviors (such that mypy
can assume a call to their specific methods would succeed in the base class), you make an equivalent @abstractmethod
in the base class.
Here's how it all looks like:
from abc import abstractmethod, ABC
from typing import AnyStr, Generic, final
class MyObject(ABC, Generic[AnyStr]):
@classmethod
@abstractmethod
def empty(cls) -> AnyStr:
pass
def __init__(self, data: AnyStr):
self.data: AnyStr = data
# Example shared method.
def is_empty(self) -> bool:
# Assume that for the sake of the example we can't do `len(self.data) == 0`, and that we need
# to check against `empty()` instead.
return self.data == self.__class__.empty()
class MyStr(MyObject[str]):
@classmethod
@final
def empty(cls) -> str:
return ""
class MyBytes(MyObject[bytes]):
@classmethod
@final
def empty(cls) -> bytes:
return b""
We make empty()
a class method instead of an instance method because it doesn't depend on an instance with particular data to know what an empty str
/ bytes
looks like.
Additionally, we make empty()
a final method so subclasses of either MyStr
or MyBytes` that want to further provide specific behavior don't get to change what is considered "empty" (as there is only one thing that can be considered empty).
All of above will typecheck under mypy --strict
.
On the caller side, they would never instantiate MyObject[str]
or MyObject[bytes]
(in fact, mypy
will prevent that, as we would want, because MyObject
doesn't have an implementation for empty()
). Instead, because you said in comments that caller will know ahead of time whether they want bytes
or str
, they instantiate MyStr
or MyBytes
directly.
Upvotes: 1