I have a base class which aims at providing common implementation and this class should not be instantiated directly. For the time being this class does not have any abstract methods. Therefore even if I declare it as abstract, someone can instantiate it (see the example below):
import abc
class BaseURL(metaclass=abc.ABCMeta):
def __init__(self, path: str) -> None:
self.path = path
def __init_subclass__(cls, schema: str) -> None:
cls.schema = schema
def __str__(self) -> str:
return f"{self.schema}://{self.path}"
class FileURL(BaseURL, schema="file")
def __init__(self, filepath: str) -> None:
if not filepath:
raise ValueError(f'Invalid filepath "{filepath}" for File URL schema.')
super().__init__(filepath)
Let’s say that the example, above, is part of an API. My intention is to allow the API user create URLs only by using the subclasses of BaseURL
(e.g. FileURL
). However, using the code above, nothing prevents someone from creating a URL using BaseURL
.
Initially I thought about decorating BaseURL.__init__
with abc.abstractmethod
but this leads to another implication which I don’t like, as every subclass of BaseURL
must override __init__
.
I started searching for an answer and I found one (on stackoverflow) which proposes the use of __new__
. So, using this approach the code above becomes like the following:
import abc
class BaseURL(metaclass=abc.ABCMeta):
def __new__(cls, *args, **kwargs):
if cls is BaseURL:
raise TypeError(f"Can't instantiate abstract class {cls.__name__}.")
return super().__new__(cls)
def __init__(self, path: str) -> None:
self.path = path
def __init_subclass__(cls, schema: str) -> None:
cls.schema = schema
def __str__(self) -> str:
return f"{self.schema}://{self.path}"
class FileURL(BaseURL, schema="file")
def __init__(self, filepath: str) -> None:
if not filepath:
raise ValueError(f'Invalid filepath "{filepath}" for File URL schema.')
super().__init__(filepath)
I’ve tested this approach and it works. However, I’m not sure if this is a legitimate solution and for that reason I’m opening the current topic.
So, any thoughts, ideas, concerns are welcome and will be greatly appreciated