我可以为我的枚举的两个不同成员使用一个别名吗

Can I have one alias for two distinct members of my enum

我真诚地希望,我遵守了所有规则和准则,并且我的 question/problem 足够全面,可以找到合适的答案。首先,一个小介绍:
对于我当前的项目,我想为树节点对象指定特定类型。为了简单起见,我将使用颜色作为类比。

我有什么

假设我有以下枚举,派生自 enum 标准库:

from enum import Enum, auto

class Colour(Enum):
  CRIMSON = auto()
  SCARLET = auto()
  RED = auto()
  BLUE = auto()

所以每个成员都有不同的价值。

我想要的

到目前为止,还不错。但我 喜欢 的是:

class Colour(Enum):
  CRIMSON = auto()
  SCARLET = auto()
  # RED = CRIMSON or SCARLET
  BLUE = auto()

因此,如果我有一个具有如下属性的对象:

>>> r = Colour.SCARLET

我想要以下语句 return True:

>>> type(r) is Colour
True
>>> r is Colour.SCARLET
True
>>> r is Colour.RED
True

当然这应该是 False:

>>> r is Colour.CRIMSON
False
>>> r is Colour.BLUE # obviously False...
False

请注意,在我的情况下,不需要能够分配类型 RED,只需分配 CRIMSONSCARLET 就足够了。

我目前(不满意)的解决方案

到目前为止我尝试的是以下内容:

class Colour(Enum):
# ... member definitions ...
  def isRed(self):
    return self is Colour.SCARLET or self is Colour.CRIMSON

但我觉得这个解决方案相当不优雅,而且使用起来很麻烦。它也没有将 CRIMSONSCARLET 的性质反映为 RED.

的阴影

我实施的另一个解决方案涉及子调用或嵌套 Colour 但在任何一种情况下我都无法满足上述所有标准,因此我没有在此处发布这些。

在使用枚举之前,我用整数表示每个节点的 Node.colour 属性:

# trees.py
# defining global constants
SCARLET = 1
CRIMSON = 2
BLUE = -1

class Node:
  def __init__(self, parent, children, col):
    self.parent = parent
    self.children = children
    self.colour = col # either 1, 2 or -1

所以我可以执行以下操作:

>>> x = Node(None, None, SCARLET)
>>> y = Node(None, None, BLUE)
>>> If x.colour is > 0:# do stuff...

但我发现这种方式非常不符合 Python 规范,而且也很不直观,因为对 REDSCARLETBLUE 等的测试经常发生,这使得代码更难一些阅读。
如果我认为没有必要,也不应该使用 int 数据类型,因为这些“颜色”没有自然顺序或其他类似 int 的属性。

为了确保我理解:

Colour.SCARLET is Color.RED
Colour.CRIMSON is color.RED

但是

Colour.SCARLET is not Colour.CRIMSON

?

如果那是正确的,那么不,你不能拥有那个。原因是 is 是对象标识,没有变通方法或技巧可以愚弄它。这是 Python 固有的,不特定于枚举。

SCARLETCRIMSON 可以作为 RED 的别名吗?或者它们是否具有独特的属性,其中之一是红色阴影?

可能对您有用的东西(使用 aenum library 显示,但使用 stdlib Enum 稍微多一点工作就可以做到):

from aenum import Enum, auto

class PrimaryColour(Enum):
    RED = auto()
    GREEN = auto()
    BLUE = auto()

class Shades(Enum):
    #
    _init_ = 'value primary'
    #
    CRIMSON = PrimaryColour.RED
    SCARLET = PrimaryColour.RED

并在使用中:

>>> list(Shades)
[<Shades.CRIMSON: 1>, <Shades.SCARLET: 2>]

>>> Shades.CRIMSON.primary is PrimaryColour.RED
True

另一种可能性:

from enum import Enum, auto

class Colour(Enum):
    #
    RED = auto()
    GREEN = auto()
    BLUE = auto()
    CRIMSON = RED
    SCARLET = RED
    AQUAMARINE = GREEN, BLUE
    #
    def is_colour(self, colour):
        return colour._value_ in self.ancestors or self._value_ in colour.ancestors
    #
    def __new__(cls, *values):
        members = list(cls)
        used_values = {m.value: m for m in members}
        # previous values are now in used_values; after isolating first
        # value, check if it has already been used
        first, *others = values
        if first in used_values:
            # it's an alias -- create a new value
            others = (first, ) + tuple(others)
            first = len(members) + 1
        member = object.__new__(cls)
        member._value_ = first
        member.ancestors = (first, ) + tuple(others)  # always it's own ancestor
        return member

并在使用中:

>>> list(Colour)
[<Colour.RED: 1>, <Colour.GREEN: 2>, <Colour.BLUE: 3>, <Colour.CRIMSON: 4>, <Colour.SCARLET: 5>, <Colour.AQUAMARINE: 6>]

>>> Colour.RED
<Colour.RED: 1>

>>> Colour.CRIMSON
<Colour.CRIMSON: 4>

>>> Colour.AQUAMARINE
<Colour.AQUAMARINE: 6>

>>> Colour.CRIMSON.is_colour(Colour.RED)
True

>>> Colour.RED.is_colour(Colour.CRIMSON)
True

>>> Colour.CRIMSON.is_colour(Colour.SCARLET)
False