Is there a way to do the same code below using Dataclass instead of Enum?
from enum import Enum, auto
class State(Enum):
val_A= auto()
val_B = auto()
val_C = auto()
The only solution I found is the following code:
from dataclasses import dataclass
@dataclass(frozen=True)
class State():
val_A:str = 'val_A'
val_B:str = 'val_B'
val_C:str = 'val_C'
thank you for the suggestions.
One approach could be to use a descriptor class, defined as below:
class Auto:
_GLOBAL_STATE = {}
__slots__ = ('_private_name', )
# `owner` is the class or type, whereas instance is an object of `owner`
def __get__(self, instance, owner):
try:
return getattr(instance, self._private_name)
except AttributeError:
_state = self.__class__._GLOBAL_STATE
_dflt = _state[owner] = _state.get(owner, 0) + 1
return _dflt
def __set_name__(self, owner, name):
self._private_name = '_' + name
def __set__(self, instance, value):
# use object.__setattr__() instead of setattr() as dataclasses
# also does, in case of a "frozen" dataclass
object.__setattr__(instance, self._private_name, value)
Usage would be as follows:
from dataclasses import dataclass
@dataclass(frozen=True)
class State:
val_A: int = Auto()
val_B: int = Auto()
val_C: int = Auto()
s = State()
print(s) # State(val_A=1, val_B=2, val_C=3)
assert s.val_B == 2
s = State(val_A=5)
assert s.val_A == 5
assert s.val_C == 3
The most performant approach I can think of, would be to replace the default values before dataclasses
is able to process the class.
Initially this is O(N)
time, as it would require iterating over all the class members (including dataclass fields) at least once. However, the real benefit is that it replaces the default values for auto
values, such as val_A: int = 1
, before the @dataclass
decorator is able to process the class.
For example, define a metaclass such as one below:
# sentinel value to detect when to replace a field's default
auto = object()
def check_auto(name, bases, cls_dict):
default = 1
for name, val in cls_dict.items():
if val == auto:
cls_dict[name] = default
default += 1
cls = type(name, bases, cls_dict)
return cls
Usage is as below:
from dataclasses import dataclass
@dataclass(frozen=True)
class State(metaclass=check_auto):
val_A: int = auto
val_B: int = auto
val_C: int = auto
s = State()
print(s) # State(val_A=1, val_B=2, val_C=3)
assert s.val_B == 2
s = State(val_A=5)
assert s.val_A == 5
assert s.val_C == 3