Even if a class is inherited from ABC
, it can still be instantiated unless it contains abstract methods.
Having the code below, what is the best way to prevent an Identifier
object from being created: Identifier(['get', 'Name'])
?
from abc import ABC
from typing import List
from dataclasses import dataclass
@dataclass
class Identifier(ABC):
sub_tokens: List[str]
@staticmethod
def from_sub_tokens(sub_tokens):
return SimpleIdentifier(sub_tokens) if len(sub_tokens) == 1 else CompoundIdentifier(sub_tokens)
@dataclass
class SimpleIdentifier(Identifier):
pass
@dataclass
class CompoundIdentifier(Identifier):
pass
You can create a AbstractDataclass
class which guarantees this behaviour, and you can use this every time you have a situation like the one you described.
@dataclass
class AbstractDataclass(ABC):
def __new__(cls, *args, **kwargs):
if cls == AbstractDataclass or cls.__bases__[0] == AbstractDataclass:
raise TypeError("Cannot instantiate abstract class.")
return super().__new__(cls)
So, if Identifier
inherits from AbstractDataclass
instead of from ABC
directly, modifying the __post_init__
will not be needed.
@dataclass
class Identifier(AbstractDataclass):
sub_tokens: List[str]
@staticmethod
def from_sub_tokens(sub_tokens):
return SimpleIdentifier(sub_tokens) if len(sub_tokens) == 1 else CompoundIdentifier(sub_tokens)
@dataclass
class SimpleIdentifier(Identifier):
pass
@dataclass
class CompoundIdentifier(Identifier):
pass
Instantiating Identifier
will raise TypeError
but not instantiating SimpleIdentifier
or CompountIdentifier
.
And the AbstractDataclass
can be re-used in other parts of the code.