Python 枚举组合

7

我希望创建一个新的枚举(IntEnum)类,基于两个现有的枚举类。这里有一个可行的解决方案:

from enum import unique, IntEnum
from itertools import chain
from collections import OrderedDict

@unique
class FirstEnumClass(IntEnum):
    a = 1
    b = 2

@unique
class SecondEnumClass(IntEnum):
    c = 3
    d = 4

# here a combined class is created:
CombinedEnumClass = unique(IntEnum('CombinedEnumClass', OrderedDict([(i.name, i.value) for i in chain(FirstEnumClass, SecondEnumClass)])))

我的问题是: 是否有一种高级的方法来实现这个目标,以便有一个适当的类定义呢?比如覆盖一些元类方法等等。我想要像这样的东西,以便还可以提供docstring:

@unique
class CombinedEnumClass(IntEnum):
    """ docstring """
    # magic needed here

有什么想法吗?谢谢!

你不需要使用OrderedDict,你可以直接使用列表推导式。 - jdehesa
我知道它是这样工作的,问题在于我正在使用PyCharm,如果只给出一个列表,它会发出警告:“期望类型为'Integral',而得到了'List[Tuple[Any, Any]]'”,而OrderedDict解决了这个问题。但是确实,即使没有那个也可以工作。无论如何,我仍然无法像类定义那样处理它。 - waszil
可能是如何扩展Python Enum?的重复问题。 - John Crawford
2个回答

6

Python 2

可以使用 vars() 的技巧来实现:

class CombinedEnum(IntEnum):
    """ doc string """
    cls = vars()
    for member in chain(list(FirstEnumClass), list(SecondEnumClass)):
        cls[member.name] = member.value
    del member, cls

print(list(CombinedEnum))

这是因为:
  • vars() 返回当前命名空间
  • 修改该命名空间会修改类
我们删除 membercls,因为我们不希望它们成为成员。

Python 3

以上内容暂时不能与Python3 3.4和3.5中的新Enum一起使用(不确定3.6是否也是如此)。解决方法是改用aenum,这样可以让Enum忽略某些名称:

from aenum import IntEnum

class CombinedEnum(IntEnum):
    """ doc string """
    _ignore_ = 'member cls'
    cls = vars()
    for member in chain(list(FirstEnumClass), list(SecondEnumClass)):
        cls[member.name] = member.value

1声明:我是Python标准库Enumenum34回溯高级枚举(aenum)库的作者。


看起来不错,但对我不起作用: File "C:\Anaconda3\lib\enum.py", line 66, in __setitem__ raise TypeError('Attempted to reuse key: %r' % key) TypeError: Attempted to reuse key: 'member'我试图跳过 cls[member.name] = member.value 这行并写一个 pass,但仍然会出现相同的错误。我使用 Python 3.5.2 和 Anaconda3。 - waszil
我在Python 2.7中尝试过,它可以工作。你知道Python 3.x的方法吗? - waszil
1
@waszil:感谢你找到那个问题。我添加了一个Python 3的解决方法。 - Ethan Furman
@EthanFurman,那么你为什么一开始就限制枚举类的子类化_ignore_ 变量是做什么用的?感谢你的库。 - Kruupös
1
@OwlMax:子类化增加了等式和身份的复杂性,我们认为允许它并不是一个净胜利;“_ignore_”变量告诉“Enum”元类从最终枚举类中丢弃这些名称。 - Ethan Furman
@EthanFurman 我在枚举方面发布了一个问题,关于“合并两个或更多的枚举类以获得更多级别的访问权限”。如果您有一些提示,我将无限感激。提前致谢。 https://dev59.com/xHEOtIcB2Jgan1znC_Mo - Gianni Spear

0

这个库显式地防止这样做:

只有在枚举没有定义任何成员的情况下才允许对枚举进行子类化。

允许对定义了成员的枚举进行子类化将导致类型和实例的一些重要不变性被违反。另一方面,允许在一组枚举之间共享一些共同行为是有意义的。

因此,我找到了一个Stackoverflow答案,使用了与你几乎相同的解决方法。我认为这是唯一的办法。


是的,看起来这似乎是唯一的方法。同时我尝试了类似于这篇文章中的做法,即对EnumMeta元类进行子类化并玩弄其__new__方法,但最终所有尝试都遇到了相同的问题:TypeError: Cannot extend enumerations - waszil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接