Я пытаюсь создать некоторые константы / enum-подобные объекты / классы с атрибутами в Python.Примерно так.
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
.
.
.
Цель - перейти от MONTH
объекта к "MONTH"
как str
.Эта часть проста, так как я могу просто использовать MONTH.__name__
.Но я также хотел бы пойти противоположным путем от "MONTH"
до MONTH
:
assert Entity("MONTH") == MONTH
Я мог бы добиться этого, выполнив следующее, но мне кажется, что мне нужно, чтобы сравнения были молниеноснымипоэтому я думаю, что есть лучшие способы.
class Entity(ABC):
def __new__(cls, arg):
try:
print(cls.__name__)
candidate = eval(arg)
if issubclass(candidate, cls):
return candidate
except:
pass
Я бы даже принял assert "MONTH" == MONTH
, но мне нужно получить класс из строки.Мне также нужно перейти от "MONTH.JANUARY"
до MONTH.JANUARY
.Сейчас я попробовал несколько разных подходов, но этот поток уже выходит из-под контроля.
EDIT1
Намного проще может быть
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
Недостатком являетсяне является полным заполнением, как есть, и завершение кода не будет работать для ссылки на PERIOD.Q1
, поскольку атрибут Q1
создается косвенно через __setattr__
EDIT2
Вот несколько примеров того, как этобудет использоваться.Производительность важна.Хотя очень сложно точно объяснить, чего я хочуЯ надеюсь, что это имеет смысл.
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