#python
#python
Вопрос:
При использовании перечислений я предпочитаю не использовать .value
везде для доступа к атрибутам элемента.
Итак, я создаю их следующим образом:
from dataclasses import dataclass
from enum import Enum
@dataclass
class Item:
foo: int
bar: int
class Collection(Item, Enum):
A = 1, 2
# Now I can use Collection.A.foo
Однако мой Item
класс имеет несколько аргументов, и для удобства чтения (и поддержки IDE) я бы предпочел создавать экземпляры элементов непосредственно в перечислении. Что-то вроде этого (что не работает, поскольку Enum хочет получить кортеж позиционных аргументов, а не экземпляр Item
):
class Collection(Item, Enum):
A = Item(1, 2)
При этом все еще пользуясь Collection.A.foo
доступом к атрибутам (вместо Collection.A.value.foo
того, что я получил бы, если бы имел Collection(Enum)
).
Или, по крайней мере, иметь возможность указать ключевые слова, которые будут использоваться для создания экземпляра Item
:
class Collection(Item, Enum):
A = dict(1, 2)
Есть ли способ достичь любого решения? Может быть, с некоторым взломом Item.__new__
?
Редактировать: я пытаюсь не использовать кортежи в перечислении. (На момент написания этого оба ответа все еще использовали кортежи для определения аргументов элементов.)
Комментарии:
1. Вы рассматривали возможность использования не перечисления, а обычной коллекции (list, set, dict, …) или модуля?
2. Проверка типов перечислений в среде IDE полезна. Но да, мы достаточно повоевали с перечислениями, чтобы ванильные классы как контейнеры (т. Е. не подклассы Enum) выглядели довольно неплохо.
Ответ №1:
Из Enum
документации Python — Вы можете попробовать переопределить __init__
or __new__
вашего класса enum и добиться желаемого результата.
Обратите внимание на следующую часть документации о том, когда переопределять какой из них:
Когда использовать __new__() против __init__()
new() необходимо использовать всякий раз, когда вы хотите настроить фактическое значение элемента перечисления. Любые другие изменения могут быть внесены либо в __new__(), либо в __init__(), причем предпочтительнее __init__().
Например, если вы хотите передать конструктору несколько элементов, но хотите, чтобы только один из них был значением (см.
__new__
переопределение)
Посмотрите на эти два примера:
Переопределение __new__
:
>>> class Coordinate(bytes, Enum):
... """
... Coordinate with binary codes that can be indexed by the int code.
... """
... def __new__(cls, value, label, unit):
... obj = bytes.__new__(cls, [value])
... obj._value_ = value
... obj.label = label
... obj.unit = unit
... return obj
... PX = (0, 'P.X', 'km')
... PY = (1, 'P.Y', 'km')
... VX = (2, 'V.X', 'km/s')
... VY = (3, 'V.Y', 'km/s')
...
>>> print(Coordinate['PY'])
Coordinate.PY
>>> print(Coordinate(3))
Coordinate.VY
переопределение __init__
:
>>> class Planet(Enum):
... MERCURY = (3.303e 23, 2.4397e6)
... VENUS = (4.869e 24, 6.0518e6)
... EARTH = (5.976e 24, 6.37814e6)
... MARS = (6.421e 23, 3.3972e6)
... JUPITER = (1.9e 27, 7.1492e7)
... SATURN = (5.688e 26, 6.0268e7)
... URANUS = (8.686e 25, 2.5559e7)
... NEPTUNE = (1.024e 26, 2.4746e7)
... def __init__(self, mass, radius):
... self.mass = mass # in kilograms
... self.radius = radius # in meters
... @property
... def surface_gravity(self):
... # universal gravitational constant (m3 kg-1 s-2)
... G = 6.67300E-11
... return G * self.mass / (self.radius * self.radius)
...
>>> Planet.EARTH.value
(5.976e 24, 6378140.0)
>>> Planet.EARTH.surface_gravity
9.802652743337129
Используя один из этих методов, вы можете создавать свои собственные, более настраиваемые классы Enum. Даже если вам также нужен ваш Item
класс как отдельный класс от enum, вы можете просто переопределить одну из этих двух функций и заставить ее работать (хотя, если Item
она была создана только ради перечисления, вам, вероятно, следует объединить ее внутри).
Ответ №2:
Я думаю, вы можете попробовать традиционный способ настройки в __init__
методе, который не требует дополнительных классов. Подробное объяснение можно найти здесь.
from enum import Enum
class Collection(Enum):
A = 1, 2
B = 3, 4
C = 5, 6
D = 7, 8
def __init__(self, foo, bar):
self.foo = foo
self.bar = bar
Пробный запуск
for item in Collection:
print(item.foo, item.bar, sep=', ')
Результат
1, 2 3, 4 5, 6 7, 8