I am trying to make some constants/enum-like objects/classes with attributes in Python. Something like this.
from abc import ABC
class Entity(ABC):
# allow *labels as attributes
class Label(ABC):
@property
def spellings(self):
raise NotImplementedError
class PeriodLabel(Label):
@property
def months(self):
raise NotImplementedError
class MONTH(Entity):
class JANUARY(Label):
spellings = ['jan.', 'january']
class FEBRUARY(Label):
spellings = ['febr.', 'february']
.
.
.
class PERIOD(Entity):
class QUARTER(PeriodLabel):
spellings = ['q1', 'q2', 'q3', 'q4']
months = 3
class HALFYEAR(PeriodLabel):
spellings = ['6m']
months = 6
.
.
.
The goal is to go from MONTH
object to "MONTH"
as a str
. That part is easy since I can just use MONTH.__name__
. But I'd also like to go the opposite way from "MONTH"
to MONTH
:
assert Entity("MONTH") == MONTH
I could achieve that by doing the following but it seems to hacky I need the comparisons to be lightning fast so I think there are better ways.
class Entity(ABC):
def __new__(cls, arg):
try:
print(cls.__name__)
candidate = eval(arg)
if issubclass(candidate, cls):
return candidate
except:
pass
I would even accept assert "MONTH" == MONTH
but I need to get the class from a string. I also need to go from "MONTH.JANUARY"
to MONTH.JANUARY
. Now I have tried a bunch of different approaches but this thread is already getting out of hand.
EDIT1
A lot simpler approach could be
from typing import List, Optional
class Label:
def __init__(self, spellings: List[str]):
self.spellings = spellings
class Entity:
def __init__(self, **labels: Label):
for k, v in labels.items():
self.__setattr__(k, v)
def get(self, entity: str, label: Optional[str] = None):
raise NotImplementedError # todo: how?
PERIOD = Entity(Q1=Label(['q1', 'q2', 'q3', 'q4']))
assert Entity.get('PERIOD') == PERIOD
assert Entity.get('PERIOD', 'Q1') == PERIOD.Q1
Downside is it is not fullfilling as it is AND code completion wont work to reference PERIOD.Q1
since Q1
attribute is indirectly created through __setattr__
EDIT2
Here are a couple of examples of how it would be used. Performance is important. It is really hard to precisely explain what I want though. I hope it makes somewhat sense.
def some_function(entity_name, label_name, spellings)
print(f"{entity_name}-{label_name}:{spellings}"
# example 1
for entity in [MONTH, PERIOD, ...]:
entity_name = entity.__name__
for label in entity:
label_name = entity.__name__
some_function(entity_name, label_name, label.spellings)
# example 2 (given entity_name, label_name as strings)
entity = Entity.get(entity_name)
label = entity.get(label_name)
if entity == PERIODS:
if label.months == 3:
# do something
# example 3 (alternative to example 1)
for label in LABELS: # ALL_LABELS is auto collecting all labels
some_function(label.entity.__name__, label.__name__, label.spellings)
# example 4 (alternative to example 2)
label = LABELS.get(entity_name, label_name)
if label.entity == PERIODS:
if label.months == 3:
# do something