Подтвердить что ты не робот

Могут ли именованные аргументы использоваться с перечислениями Python?

Пример:

class Planet(Enum):

    MERCURY = (mass: 3.303e+23, radius: 2.4397e6)

    def __init__(self, mass, radius):
        self.mass = mass       # in kilograms
        self.radius = radius   # in meters

Ссылка: https://docs.python.org/3/library/enum.html#planet

Почему я хочу это сделать? Если в списке конструкторов есть несколько примитивных типов (int, bool), было бы неплохо использовать именованные аргументы.

4b9b3361

Ответ 1

Пока вы не можете использовать именованные аргументы, как вы описываете с перечислениями, вы можете получить аналогичный эффект с помощью namedtuple mixin:

from collections import namedtuple
from enum import Enum

Body = namedtuple("Body", ["mass", "radius"])

class Planet(Body, Enum):

    MERCURY = Body(mass=3.303e+23, radius=2.4397e6)
    VENUS   = Body(mass=4.869e+24, radius=6.0518e6)
    EARTH   = Body(mass=5.976e+24, radius=3.3972e6)
    # ... etc.

... который, на мой взгляд, более чист, поскольку вам не нужно писать метод __init__.

Пример использования:

>>> Planet.MERCURY
<Planet.MERCURY: Body(mass=3.303e+23, radius=2439700.0)>
>>> Planet.EARTH.mass
5.976e+24
>>> Planet.VENUS.radius
6051800.0

Обратите внимание, что в соответствии с документами типы смешения должны появляться перед Enum в последовательности оснований".

Ответ 2

Принятый ответ @zero-piraeus может быть немного расширен, чтобы разрешить также аргументы по умолчанию. Это очень удобно, если у вас есть большое перечисление с большинством записей, имеющих одинаковое значение для элемента.

class Body(namedtuple('Body', "mass radius moons")):
    def __new__(cls, mass, radius, moons=0):
        return super().__new__(cls, mass, radius, moons)
    def __getnewargs__(self):
        return (self.mass, self.radius, self.moons)

class Planet(Body, Enum):

    MERCURY = Body(mass=3.303e+23, radius=2.4397e6)
    VENUS   = Body(mass=4.869e+24, radius=6.0518e6)
    EARTH   = Body(5.976e+24, 3.3972e6, moons=1)

Остерегайтесь, что травление не будет работать без __getnewargs__.

class Foo:
    def __init__(self):
        self.planet = Planet.EARTH  # pickle error in deepcopy

from copy import deepcopy

f1 = Foo()
f2 = deepcopy(f1)  # pickle error here

Ответ 3

Если вы выходите за пределы namedtuple mix-in, проверьте aenum библиотеку (написанную тем же человеком, который написал stdlib Enum и enum34). Помимо наличия нескольких дополнительных колоколов и свистков для Enum, он также поддерживает NamedConstant и метаклассовый namedtuple.

Используя aenum.Enum, приведенный выше код может выглядеть так:

from aenum import Enum, enum, _reduce_ex_by_name

class Planet(Enum, init='mass radius'):
    MERCURY = enum(mass=3.303e+23, radius=2.4397e6)
    VENUS   = enum(mass=4.869e+24, radius=6.0518e6)
    EARTH   = enum(mass=5.976e+24, radius=3.3972e6)
    # replace __reduce_ex__ so pickling works
    __reduce_ex__ = _reduce_ex_by_name

и при использовании:

--> for p in Planet:
...     print(repr(p))
<Planet.MERCURY: enum(radius=2439700.0, mass=3.3030000000000001e+23)>
<Planet.EARTH: enum(radius=3397200.0, mass=5.9760000000000004e+24)>
<Planet.VENUS: enum(radius=6051800.0, mass=4.8690000000000001e+24)>

--> print(Planet.VENUS.mass)
4.869e+24