类型为“type”的枚举类作为mixin

Posted

技术标签:

【中文标题】类型为“type”的枚举类作为mixin【英文标题】:Enum class with type `type` as mixin 【发布时间】:2021-03-16 07:50:19 【问题描述】:

docs 声明可以通过将某个类型作为 mixin 添加到枚举类中来创建一个其成员完全属于某种类型的枚举类:

虽然 :class:IntEnum 是 :mod:enum 模块的一部分,但它会是 独立实现非常简单::

class IntEnum(int, Enum):
    pass

这演示了如何定义相似的派生枚举;为了 例如一个 :class:StrEnum 混入 :class:str 而不是 :class:int.

一些规则:

    当子类化 :class:Enum 时,混合类型必须出现在 :class:Enum 本身之前的基序列中,如 :class:IntEnum 上面的例子。 虽然 :class:Enum 可以有任何类型的成员,但一旦混合了其他类型,所有成员都必须具有该类型的值, 例如:class:int 以上。此限制不适用于 仅添加方法而不指定其他类型的混入。 当混入另一种数据类型时,:attr:value 属性与枚举成员本身不同,尽管它是等价的并且会比较相等。 %-style格式:%s%r分别调用:class:Enum类的:meth:__str__和:meth:__repr__;其他代码(例如 如 %i%h for IntEnum) 将枚举成员视为其混合 输入。 :ref:Formatted string literals <f-strings>、:meth:str.format和:func:format将使用混合类型的:meth:__format__ 除非 :meth:__str__ 或 :meth:__format__ 在 子类,在这种情况下被覆盖的方法或 :class:Enum 会用到方法。使用 !s 和 !r 格式代码强制使用 :class:Enum 类的 :meth:__str__ 和 :meth:__repr__ 方法。

我试图创建一个枚举,其成员是类型(type 类型的对象),但由于 type 是一个特殊的类(它是一个元类而不是一个类),我得到了一堆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 类层次结构;每个子类代表不同的卡片类型。但是,游戏中的卡牌类型与Card的子类之间的映射可能不是一对一的;例如,可能有一些中间抽象类用作实现帮助,但不代表实际的卡片类型。

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

【问题讨论】:

即使事实证明这是可能的,生成的枚举成员的语义也会过于混乱。不同的设计会是更好的主意。 在您的示例中,您重复使用密钥EGGS,这会导致错误。 @Anakhand:反之亦然。你可以有FOO=1BAR=1,但不能有FOO=1FOO=2 别名是同一个值的另一个名字,不是两个值的同一个名字 啊,你是完全正确的,出于某种原因,我脑子里突然出现了这种情况。我已经更正了这个例子。无论如何,这与问题无关。 【参考方案1】:

简短回答:不,目前无法将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 Enumenum34 backport 和 Advanced Enumeration (aenum) 库的作者。

【讨论】:

我意识到这可能是一个 XY 问题,所以我在编辑我的问题时解释了我的具体用例。

以上是关于类型为“type”的枚举类作为mixin的主要内容,如果未能解决你的问题,请参考以下文章

enum枚举

类型“Favorites.Type”不能符合“Encodable”;只有结构/枚举/类类型可以符合协议

type() 不能符合 View;只有结构/枚举/类类型可以符合协议

C++类模板:不是类型名称、静态或枚举数

java枚举类-根据key获取value及根据value获取key

枚举类