如何创建一个自引用的Python 3枚举



我可以创建一个枚举类RockPaperScissors,使ROCK.value == "rock"ROCK.beats == SCISSORS,其中ROCKSCISSORS都是RockPaperScissors中的常量?

枚举成员是该类型的实例。这意味着您可以只使用常规属性:

from enum import Enum
class RockPaperScissors(Enum):
    Rock = "rock"
    Paper = "paper"
    Scissors = "scissors"
    @property
    def beats(self):
        lookup = {
            RockPaperScissors.Rock: RockPaperScissors.Scissors,
            RockPaperScissors.Scissors: RockPaperScissors.Paper,
            RockPaperScissors.Paper: RockPaperScissors.Rock,
        }
        return lookup[self]

通过仔细挑选成员的顺序,每个成员都可以简单地描述为用property击败前一个成员。

from enum import Enum
class RPS(Enum):
    Rock = 0
    Paper = 1
    Scissor = 2
    @property
    def beats(self):
        return list(RPS)[self.value - 1]
for v in RPS:
    print(v.name, 'beats', v.beats.name)

输出

Rock beats Scissor
Paper beats Rock
Scissor beats Paper

在类创建过程中让Enum成员相互引用有点棘手;诀窍是知道每个成员在添加到Enum本身之前都已创建和初始化。 这意味着您可以检查正在创建的Enum的状态,并对正在创建的成员和已创建的成员进行调整。

这里的基本问题是在成员之间进行循环引用,我们可以通过修改每个新成员的圆来解决这个问题:

class RPS(Enum):
    Rock = "rock"
    Paper = "paper"
    Scissors = "scissors"
    def __init__(self, value):
        if len(self.__class__):
            # make links
            all = list(self.__class__)
            first, previous = all[0], all[-1]
            first.beats = self
            self.beats = previous

和使用中:

>>> print(RPS.Rock.beats)
RPS.Scissors
>>> print(RPS.Paper.beats)
RPS.Rock
>>> print(RPS.Scissors.beats)
RPS.Paper

披露:我是Python stdlib Enumenum34向后移植和高级枚举(aenum(库的作者。

像这样的事情呢:

from enum import IntEnum
class RPS(IntEnum):
    Rock = 1
    Paper = 2
    Scissor = 3
    def __lt__(self, other):
        if self == RPS.Scissor and other == RPS.Rock:
            return True
        if self == RPS.Rock and other == RPS.Scissor:
            return False
        return self.value < other.value
    def __gt__(self, other):
        if self == RPS.Rock and other == RPS.Scissor:
            return True
        if self == RPS.Scissor and other == RPS.Rock:
            return False
        return self.value > other.value

这不是 Rock.beats,但谁在枚举(或类(上击败谁似乎更合乎逻辑,击败剪刀不是 Rock 的继承属性,这是我们定义 RPS 的方式(如果您决定尝试其他方法,也可以反过来(使用 python 方法 ge(如果需要,您可以实现其余部分(,您可以自然地进行比较,如下所示:

from itertools import combinations
members = list(RPS)
for pair in combinations(members, 2):
    print(f'{pair[1].name} < {pair[0].name} ? {pair[1] < pair[0]}')
    print(f'{pair[0].name} < {pair[1].name} ? {pair[0] < pair[1]}')
    print(f'{pair[1].name} > {pair[0].name} ? {pair[1] > pair[0]}')
    print(f'{pair[0].name} > {pair[1].name} ? {pair[0] > pair[1]}')

其中输出:

Paper < Rock ? False
Rock < Paper ? True
Paper > Rock ? True
Rock > Paper ? False
Scissor < Rock ? True
Rock < Scissor ? False
Scissor > Rock ? False
Rock > Scissor ? True
Scissor < Paper ? False
Paper < Scissor ? True
Scissor > Paper ? True
Paper > Scissor ? False

你试过吗?

from enum import IntEnum
class RPS(IntEnum):
    Rock = 1 
    Paper = 2 
    Scissor = 3
RPS.Rock.beats = RPS.Scissor
RPS.Paper.beats = RPS.Rock
RPS.Scissor.beats = RPS.Paper
for i in [RPS.Rock,RPS.Paper,RPS.Scissor]:
    print(i, "beats", i.beats)

输出:

RPS.Rock beats RPS.Scissor
RPS.Paper beats RPS.Rock
RPS.Scissor beats RPS.Paper

是的。您可以。


在python中,所有(*(事物都是对象,您可以在它们上附加更多属性:

def func():
    pass
func.result = 42
print(func.result)  # 42

*( 很少有例外<</p>

div class="one_answers">

为了完整起见,也可以这样做。它更具描述性,但需要这个getattr调用,我个人不是最大的粉丝。

from enum import Enum
class RockPaperScissors(Enum):
    ROCK = ("rock", "scissors")
    PAPER = ("paper", "rock")
    SCISSORS = ("scissors", "paper")
    def __init__(self, value, beats):
        self._value_ = value
        self._beats = beats
    @property
    def beats(self):
        return getattr(RockPaperScissors, self._beats.upper())

最新更新