枚举 class,类型为 `type` 作为 mixin

Enum class with type `type` as mixin

docs 声明可以创建一个枚举 class,其成员恰好是某种类型,方法是将该类型作为混合类型添加到枚举 class 中:

While :class:IntEnum is part of the :mod:enum module, it would be very simple to implement independently::

class IntEnum(int, Enum):
    pass

This demonstrates how similar derived enumerations can be defined; for example a :class:StrEnum that mixes in :class:str instead of :class:int.

Some rules:

  1. When subclassing :class:Enum, mix-in types must appear before :class:Enum itself in the sequence of bases, as in the :class:IntEnum example above.
  2. While :class:Enum can have members of any type, once you mix in an additional type, all the members must have values of that type, e.g. :class:int above. This restriction does not apply to mix-ins which only add methods and don't specify another type.
  3. When another data type is mixed in, the :attr:value attribute is not the same as the enum member itself, although it is equivalent and will compare equal.
  4. %-style formatting: %s and %r call the :class:Enum class's :meth:__str__ and :meth:__repr__ respectively; other codes (such as %i or %h for IntEnum) treat the enum member as its mixed-in type.
  5. :ref:Formatted string literals <f-strings>, :meth:str.format, and :func:format will use the mixed-in type's :meth:__format__
    unless :meth:__str__ or :meth:__format__ is overridden in the subclass, in which case the overridden methods or :class:Enum methods will be used. Use the !s and !r format codes to force usage of the :class:Enum class's :meth:__str__ and :meth:__repr__ methods.

我试图创建一个枚举,其成员是类型(type 类型的对象),但由于 type 是一个特殊的 class(它是一个元 class 多于 class),我收到了一堆错误,例如 mro__new____init__ 等的参数不匹配:

import enum

class A: pass

class B: pass

class C(B): pass


class MyTypes(type, enum.Enum):
    SPAM = A
    EGGS = C

错误示例:

Traceback (most recent call last):
  File "<file>", line 10, in <module>
    class MyTypes(type, enum.Enum):
  File "<file>", line 182, in __new__
    dynamic_attributes = {k for c in enum_class.mro()
TypeError: descriptor 'mro' of 'type' object needs an argument

有办法实现吗?


我意识到这可能是一个 XY 问题,所以我将解释我的具体示例。在我正在开发的纸牌游戏中,我有一个 Card class 层次结构;每个 subclasses 代表不同的卡片类型。但是,游戏中的卡牌类型与Card的子class卡牌类型之间的映射可能不是一对一的;例如,可能有一些中间抽象 classes 作为实现帮助,但不代表实际的卡类型。

所以我想做一个包含所有游戏卡类型的枚举,我希望成员是实现这些类型的实际具体子classes,这样我就可以轻松地互操作枚举成员和实际的 classes:这个想法是访问 class 属性,实例化它们,等等,而不必每次都使用 .value (因为在某些情况下,我可能不得不使用一些东西是枚举的成员或 class 本身)。

简短回答:不,目前无法将 type 混入枚举。


混合数据类型的目的有两个:

  • 确保所有成员都是该类型
class Number(int, Enum):
    ONE = 1
    TWO = 2
    THREE = 'three'
# ValueError: invalid literal for int() with base 10: 'three'
  • 使成员可以直接用作该类型
class Number(int, Enum):
    ONE = 1
    TWO = 2
Number.ONE + 2
# 3

如您所述,type 不是一种数据类型——它是一种元类。它需要特殊支持才能工作。您的用例是什么?


披露:我是 Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) 库的作者。