带有属性的python枚举

时间:2012-10-01 19:53:03

标签: python enums

考虑:

class Item:
   def __init__(self, a, b):
       self.a = a
       self.b = b

class Items:
    GREEN = Item('a', 'b')
    BLUE = Item('c', 'd')

有没有办法让简单枚举的想法适应这种情况? (参见this question)理想情况下,就像在Java中一样,我想将它们全部塞进一个类中。

Java模型:

enum EnumWithAttrs {
    GREEN("a", "b"),
    BLUE("c", "d");

    EnumWithAttrs(String a, String b) {
      this.a = a;
      this.b = b;
    }

    private String a;
    private String b;

    /* accessors and other java noise */
}

6 个答案:

答案 0 :(得分:9)

Python 3.4有一个new Enum data type(已backported as enum34enhanced as aenum 1 )。 enum34aenum 2 都可以轻松支持您的使用案例:

[aenum py2 / 3]

import aenum
class EnumWithAttrs(aenum.AutoNumberEnum):
    _init_ = 'a b'
    GREEN = 'a', 'b'
    BLUE = 'c', 'd'

[enum34 py2 / 3或stdlib enum 3.4 +]

import enum
class EnumWithAttrs(enum.Enum):

    def __new__(cls, *args, **kwds):
        value = len(cls.__members__) + 1
        obj = object.__new__(cls)
        obj._value_ = value
        return obj

    def __init__(self, a, b):
        self.a = a
        self.b = b

    GREEN = 'a', 'b'
    BLUE = 'c', 'd'

并在使用中:

--> EnumWithAttrs.BLUE
<EnumWithAttrs.BLUE: 1>

--> EnumWithAttrs.BLUE.a
'c'

1 披露:我是Python stdlib Enumenum34 backportAdvanced Enumeration (aenum)图书馆的作者。

2 aenum还支持NamedConstants和基于元类的NamedTuples

答案 1 :(得分:5)

这是我认为比其他方法更简单但又具有最大灵活性的另一种方法:

compromise

按预期工作:

from collections import namedtuple
from enum import Enum

class Status(namedtuple('Status', 'name description'), Enum):
    READY = 'ready', 'I am ready to do whatever is needed'
    ERROR = 'error', 'Something went wrong here'

    def __str__(self) -> str:
        return self.name

打印:

print(Status.READY)
print(repr(Status.READY))
print(Status.READY.description)
print(Status.READY.value)

您将获得namedtuple Enum的精华。

答案 2 :(得分:4)

使用namedtuple

from collections import namedtuple

Item = namedtuple('abitem', ['a', 'b'])

class Items:
    GREEN = Item('a', 'b')
    BLUE = Item('c', 'd')

答案 3 :(得分:2)

对于Python 3:

class Status(Enum):
    READY = "ready", "I'm ready to do whatever is needed"
    ERROR = "error", "Something went wrong here"

    def __new__(cls, *args, **kwds):
        obj = object.__new__(cls)
        obj._value_ = args[0]
        return obj

    # ignore the first param since it's already set by __new__
    def __init__(self, _: str, description: str = None):
        self._description_ = description

    def __str__(self):
        return self.value

    # this makes sure that the description is read-only
    @property
    def description(self):
        return self._description_

您可以按类型将其用作标准枚举或工厂:

print(Status.READY)
# ready
print(Status.READY.description)
# I'm ready to do whatever is needed
print(Status("ready")) # this does not create a new object
# ready

答案 4 :(得分:1)

受到其他一些答案的启发,我找到了一种方法,可以尽可能“透明地”将附加字段包含到枚举中,从而克服了其他方法的一些缺点。一切正常,就好像没有附加字段一样。

枚举就像元组一样是不可变的,枚举的值就像没有附加字段一样,它的工作方式就像带有 auto() 的普通枚举,并且可以通过值选择枚举。

import enum

# Common base class for all enums you want to create with additional fields (you only need this once)
class EnumFI(enum.Enum):

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        cls._values = []

    def __new__(cls, *args, **kwargs):
        value = args[0]
        if isinstance(value, enum.auto):
            if value.value == enum._auto_null:
                value.value = cls._generate_next_value_(None, 1, len(cls.__members__), cls._values[:])  # Note: This just passes None for the key, which is generally okay
            value = value.value
            args = (value,) + args[1:]
        cls._values.append(value)
        instance = cls._member_type_.__new__(cls, *args, **kwargs)
        instance._value_ = value
        return instance

然后您可以在代码中的任何位置执行:

from enum import auto
from collections import namedtuple

class Color(namedtuple('ColorTuple', 'id r g b'), EnumFI):
    GREEN = auto(), 0, 255, 0
    BLUE = auto(), 0, 0, 255

示例输出:

In[4]: Color.GREEN
Out[4]: <Color.GREEN: 1>

In[5]: Color.GREEN.value
Out[5]: 1

In[6]: Color.GREEN.r
Out[6]: 0

In[7]: Color.GREEN.g
Out[7]: 255

In[8]: Color.GREEN.b
Out[8]: 0

In[9]: Color.GREEN.r = 8
Traceback (most recent call last):
  File "/home/phil/anaconda3/envs/dl/lib/python3.6/site-packages/IPython/core/interactiveshell.py", line 3326, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-9-914a059d9d3b>", line 1, in <module>
    Color.GREEN.r = 8
AttributeError: can't set attribute

In[10]: Color(2)
Out[10]: <Color.BLUE: 2>

In[11]: Color['BLUE']
Out[11]: <Color.BLUE: 2>

答案 5 :(得分:0)

对于小型枚举@property 可能有效:

class WikiCfpEntry(Enum):
    '''
    possible supported storage modes
    '''
    EVENT = "Event"      
    SERIES = "Series"
    
    @property
    def urlPrefix(self):
        baseUrl="http://www.wikicfp.com/cfp"
        if self==WikiCfpEntry.EVENT:
            url= f"{baseUrl}/servlet/event.showcfp?eventid="
        elif self==WikiCfpEntry.SERIES:
            url= f"{baseUrl}/program?id="
        return url