使IntEnum成员可以调用,同时保持他们的比较行为

时间:2015-05-18 17:55:37

标签: python python-3.x

让我们说Color类是一个枚举:

from enum import IntEnum
class Color(IntEnum):
    red = 1
    blue = 2
    green = 3

现在,让我们说我希望将每个成员与一个功能相关联并保持其价值。即做这样的事情

Colors.red()

会调用我以某种方式分配的功能。 并保持IntEnum行为,意味着

Colors.red == 1

仍会返回True

我想我需要为枚举定义一个新的超类,但是如何?

我唯一能做到的就是:

class Colors(Enum):
    red = (1, f)
    blue = (2, g)
    green = (3, h)

其中f gh是函数。 然而。使用此方法,访问该值的唯一方法是

Colors.red.value[0]

要调用该函数,这是

Colors.red.value[1](args)

在我看来,这很丑陋,尤其是当名字有点长,当你需要多次调用函数并且有很多参数时

那么有没有办法做我想做或做的事我需要坚持使用uggly版本?

2 个答案:

答案 0 :(得分:2)

简单明了,虽然是可读性和可理解性的噩梦。通过提供__call__方法简单地实现Python可调用接口。将它埋在某个模块中,实现__all__并且永远不会导入除Color类之外的任何内容。

from enum import IntEnum
f1 = lambda x: x
f2 = lambda x: x*x
f3 = lambda x: x*x*x

class Color(IntEnum):
    red = 1
    blue = 2
    green = 3
    def __call__(self, *args, **kwargs):
        # alternatively some generic function of self.value
        return LUT[self](*args, **kwargs)


LUT = {Color.red: f1, Color.blue: f2, Color.green: f3}

assert Color.red(1) == 1
assert Color.blue(2) == 4
assert Color.green(3) == 27
assert Color.blue > Color.red

编辑: 更好(更可读,更少hacky等)将在Enum上定义显式可调用接口。

class Color(IntEnum):
    red = 1
    blue = 2
    green = 3
    def get_something(self, x):
        # e.g.
        return x ** self.value

此枚举的任何用户都会执行以下操作:

o = Obj()
o.color.get_something(123)

使用get_something的正确名称可读,易于理解pylint和其他静态分析工具,并且不易出错。

答案 1 :(得分:1)

反对我更好的判断,我决定解开将Enum组合在一起的神奇线索。这就是我想出的:

from enum import Enum

class FuncEnum(Enum):
    def __init__(self, val, func=None):
        self.val = val
        self.func = func or lambda: None
    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)
    def __eq__(self, other):
        return self.val == other
    def __ge__(self, other):
        return self.val >= other
    # and etc... not sure how @total_ordering will work here!

class Color(FuncEnum):
    red = (1, lambda x: x**2)
    blue = (2, lambda x: x**3)
    green = (3, lambda x: x**4)

Color.red(4)  # 16
Color.red == 1  # True
Color.blue(2)  # 8

这也支持重新分配,所以事后:

Color.red.func = str.lower
Color.red("LOWERCASE")  # "lowercase"