python enums with attributes

后端 未结 4 1129
自闭症患者
自闭症患者 2020-12-01 12:52

Consider:

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

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


        
相关标签:
4条回答
  • 2020-12-01 12:56

    Here's another approach which I think is simpler than the others, but allows the most flexibility:

    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
    

    Works as expected:

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

    prints:

    ready
    <Status.READY: Status(name='ready', description='I am ready to do whatever is needed')>
    I am ready to do whatever is needed
    Status(name='ready', description='I am ready to do whatever is needed')
    

    You get the best of namedtuple and Enum.

    0 讨论(0)
  • 2020-12-01 13:02

    For 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_
    

    And you can use it as a standard enum or factory by type:

    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
    
    0 讨论(0)
  • 2020-12-01 13:04

    Before Python 3.4 and the addition of the excellent enum module, a good choice would have been to use a namedtuple:

    from collections import namedtuple
    
    Item = namedtuple('abitem', ['a', 'b'])
    
    class Items:
        GREEN = Item('a', 'b')
        BLUE = Item('c', 'd')
    

    These days, any supported version of Python has enum, so please use that module. It gives you a lot more control over how each enum value is produced.

    If you give each item a tuple of values, then these are passed to the __init__ method as separate (positional) arguments, which lets you set additional attributes on the enum value:

    from enum import Enum
    
    class Items(Enum):
        GREEN = ('a', 'b')
        BLUE = ('c', 'd')
    
        def __init__(self, a, b):
            self.a = a
            self.b = b
    

    This produces enum entries whose value is the tuple assigned to each name, as well as two attributes a and b:

    >>> Items.GREEN, Items.BLUE
    (<Items.GREEN: ('a', 'b')>, <Items.BLUE: ('c', 'd')>)
    >>> Items.BLUE.a
    'c'
    >>> Items.BLUE.b
    'd'
    >>> Items(('a', 'b'))
    <Items.GREEN: ('a', 'b')>
    

    Note that you can look up each enum value by passing in the same tuple again.

    If the first item should represent the value of each enum entry, use a __new__ method to set _value_:

    from enum import Enum
    
    class Items(Enum):
        GREEN = ('a', 'b')
        BLUE = ('c', 'd')
    
        def __new__(cls, a, b):
            entry = object.__new__(cls) 
            entry.a = entry._value_ = a  # set the value, and the extra attribute
            entry.b = b
            return entry
    
        def __repr__(self):
            return f'<{type(self).__name__}.{self.name}: ({self.a!r}, {self.b!r})>'
    

    I added a custom __repr__ as well, the default only includes self._value_. Now the value of each entry is defined by the first item in the tuple, and can be used to look up the enum entry:

    >>> Items.GREEN, Items.BLUE
    (<Items.GREEN: ('a', 'b')>, <Items.BLUE: ('c', 'd')>)
    >>> Items.BLUE.a
    'c'
    >>> Items.BLUE.b
    'd'
    >>> Items('a')
    <Items.GREEN: ('a', 'b')>
    

    See the section on __init__ vs. __new__ in the documentation for further options.

    0 讨论(0)
  • 2020-12-01 13:10

    Python 3.4 has a new Enum data type (which has been backported as enum34 and enhanced as aenum1). Both enum34 and aenum2 easily support your use case:

    [aenum py2/3]

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

    [enum34 py2/3 or 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'
    

    And in use:

    --> EnumWithAttrs.BLUE
    <EnumWithAttrs.BLUE: 1>
    
    --> EnumWithAttrs.BLUE.a
    'c'
    

    1 Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.

    2 aenum also supports NamedConstants and metaclass-based NamedTuples.

    0 讨论(0)
提交回复
热议问题