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
Advertisement
Answer
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.