"Other"/python 枚举上的默认名称行为

"Other"/Default-name behavior on a python Enum

我试图在 python "enum" 中实现以下行为(到目前为止没有成功):

给定枚举 class

class MyEnum(enum.Enum):
    A=1
    B=2
    C=3

我想要一个 "Other" 成员,这样 MyEnum(5) 将被解释为 "Other",同时保留值 5,或者

>>> str(MyEnum(5))
... "<MyEnum.Other: 5>"

我想过重写 _missing_ 函数,但我不知道如何在不重写 [=17] 的情况下创建 MyEnum 的 "custom" 实例=].

建议将不胜感激。

编辑: 在一些未能准确理解我的问题的评论之后,我不希望枚举有默认值,因为此默认值不会保留该值(我希望保留)。我只希望使用默认名称接受该值。

这看起来像您要的:

>>> class Yep(enum.IntFlag):
...  A = 1
...  B = 2
... 
>>> Yep(5)
<Yep.4|A: 5>

所以,Yep(5)有效的,但是不会有Yep.Other魔法成员。

俗话说,如果你想做某事...我创建了以下枚举子class(我没有添加任何新成员,所以这是允许的):

class DefaultNameEnum(Enum):
    """Support for "Other"/default-name values"""

    @classmethod
    def _missing_(cls, value):
        possible_member = cls._value2member_map_.get(value, None)

        if possible_member is None:
            possible_member = cls._create_pseudo_member_(value)

        return possible_member

    @classmethod
    def _create_pseudo_member_(cls, value):
        """
        Create a default-name member.
        """

        default_member = cls._value2member_map_.get(None, None)

        if default_member is None:
            raise ValueError("%r is not a valid %s" % (value, cls.__name__))

        # construct a singleton enum pseudo-member
        other_member = object.__new__(cls)
        other_member._name_ = default_member._name_
        other_member._value_ = value

        # use setdefault in case another thread already created a composite
        # with this value
        other_member = cls._value2member_map_.setdefault(value, other_member)

        return other_member

    def __eq__(self, other):
        """Overrides the default implementation"""
        if isinstance(other, DefaultNameEnum):
            return self._name_ == other._name_
        return False

    def __ne__(self, other):
        return not self == other

这是基于 Flag 枚举子 class。实际上,它的用法非常简单 - 只需定义为 None 您希望将其作为默认名称即可。最好用一个例子来说明 - 考虑 class:

class ABC(DefaultNameEnum):
    A = 1
    B = 2
    C = 3

    Other = None

然后,以下控制台调用将给出:

>>> print([repr(mem) for mem in ABC])    
... ['<ABC.A: 1>', '<ABC.B: 2>', '<ABC.C: 3>', '<ABC.Other: None>']
>>> ABC(123)
... '<ABC.Other: 123>'
>>> ABC(1) == ABC(2)
... False
>>> ABC(123) == ABC.Other
... True
>>> ABC(123) == ABC(1374)
... True

如果您希望获取并使用此实现,请注意以下几点:

  1. 最后一行中的行为可能需要也可能不需要 - 取决于您的使用情况。如果这是不需要的用法,只需更改 __eq__ 方法以在 self._value_other._value_None.

  2. 时比较名称
  3. 如果您使用此 class,为了可表示性,您可能希望默认值的 __repr__ 输出 '<ABC.Other>' 而不是 '<ABC.Other: None>'None 值的情况下。这可以通过覆盖 __repr__ 方法轻松实现。

  4. 如果您没有定义默认成员,class 将在根据未知值调用它时引发异常(就像任何 Enum subclass).

我还想指出,在上面的实现中,我更愿意使用拆分成员,例如 _default_name__default_value_member_ 而不是分配 None,但是唉enum 模块不允许为 Enum subclasses 定义新的拆分成员。