创建一个抽象的枚举类



我正在尝试使用抽象方法创建一个抽象枚举(Flag(。我的最终目标是能够根据我定义的基本枚举来创建复合枚举的字符串表示。我能够在无需摘要的情况下获得此功能。

这是基本的Flag类,也是一个示例实现:

from enum import auto, Flag
class TranslateableFlag(Flag):
    @classmethod
    def base(cls):
        pass
    def translate(self):
        base = self.base()
        if self in base:
            return base[self]
        else:
            ret = []
            for basic in base:
                if basic in self:
                    ret.append(base[basic])
            return " | ".join(ret)
class Students(TranslateableFlag):
    ALICE = auto()
    BOB = auto()
    CHARLIE = auto()
    ALL = ALICE | BOB | CHARLIE
    @classmethod
    def base(cls):
        return {Students.ALICE: "Alice", Students.BOB: "Bob",
                Students.CHARLIE: "Charlie"}

一个示例用法是:

((Students.ALICE | Students.BOB).translate())
[Out]: 'Alice | Bob'

切换到TranslateableFlag(Flag, ABC)由于掌骨冲突而失败。(我不理解这篇文章 - 使用Abcmeta和Enummeta的抽象枚举类,所以我不确定它是否在回答我的问题(。

我想以这种功能以某种方式获得这样的功能:

@abstractclassmethod
@classmethod
    def base(cls):
        pass

有可能实现此目标吗?

这是如何使用Abcmeta和Enummeta将接受的答案改编成问题枚举枚举类,以创建您想要的抽象Enum类。

from abc import abstractmethod, ABC, ABCMeta
from enum import auto, Flag, EnumMeta

class ABCEnumMeta(ABCMeta, EnumMeta):
    def __new__(mcls, *args, **kw):
        abstract_enum_cls = super().__new__(mcls, *args, **kw)
        # Only check abstractions if members were defined.
        if abstract_enum_cls._member_map_:
            try:  # Handle existence of undefined abstract methods.
                absmethods = list(abstract_enum_cls.__abstractmethods__)
                if absmethods:
                    missing = ', '.join(f'{method!r}' for method in absmethods)
                    plural = 's' if len(absmethods) > 1 else ''
                    raise TypeError(
                       f"cannot instantiate abstract class {abstract_enum_cls.__name__!r}"
                       f" with abstract method{plural} {missing}")
            except AttributeError:
                pass
        return abstract_enum_cls

class TranslateableFlag(Flag, metaclass=ABCEnumMeta):
    @classmethod
    @abstractmethod
    def base(cls):
        pass
    def translate(self):
        base = self.base()
        if self in base:
            return base[self]
        else:
            ret = []
            for basic in base:
                if basic in self:
                    ret.append(base[basic])
            return " | ".join(ret)

class Students1(TranslateableFlag):
    ALICE = auto()
    BOB = auto()
    CHARLIE = auto()
    ALL = ALICE | BOB | CHARLIE
    @classmethod
    def base(cls):
        return {Students1.ALICE: "Alice", Students1.BOB: "Bob",
                Students1.CHARLIE: "Charlie"}

# Abstract method not defined - should raise TypeError.
class Students2(TranslateableFlag):
    ALICE = auto()
    BOB = auto()
    CHARLIE = auto()
    ALL = ALICE | BOB | CHARLIE
#    @classmethod
#    def base(cls):
#        ...

结果:

Traceback (most recent call last):
  ...
TypeError: cannot instantiate abstract class 'Students2' with abstract method 'base'

这是Python 3.8的接受答案的修复。唯一的更改是ABCEnumMeta。其余的是从原始答案中复制的,以提供可运行的示例。也对Python 3.6.2。

进行了测试。
from abc import abstractmethod, ABC, ABCMeta
from enum import auto, Flag, EnumMeta

class ABCEnumMeta(EnumMeta, ABCMeta):
    pass

class TranslateableFlag(Flag, metaclass=ABCEnumMeta):
    @classmethod
    @abstractmethod
    def base(cls):
        pass
    def translate(self):
        base = self.base()
        if self in base:
            return base[self]
        else:
            ret = []
            for basic in base:
                if basic in self:
                    ret.append(base[basic])
            return " | ".join(ret)

class Students1(TranslateableFlag):
    ALICE = auto()
    BOB = auto()
    CHARLIE = auto()
    ALL = ALICE | BOB | CHARLIE
    @classmethod
    def base(cls):
        return {Students1.ALICE: "Alice", Students1.BOB: "Bob",
                Students1.CHARLIE: "Charlie"}

class Students2(TranslateableFlag):
    ALICE = auto()
    BOB = auto()
    CHARLIE = auto()
    ALL = ALICE | BOB | CHARLIE
    
# Abstract method not defined - should raise TypeError.
#    @classmethod
#    def base(cls):
#        ...

如果目标只是更改Students1__str__输出,则不需要使用抽象类:

from enum import auto, Flag
from functools import reduce
class TranslateableFlag(Flag):
    def __init__(self, value):
        self.translated = self.name.title()
    def __str__(self):
        cls = self.__class__
        total = self._value_
        i = 1
        bits = set()
        while i <= total:
            bits.add(i)
            i *= 2
        members = [m for m in cls if m._value_ in bits]
        return '%s' % (
                ' | '.join([str(m.translated) for m in members]),
                )
class Students1(TranslateableFlag):
    ALICE = auto()
    BOB = auto()
    CHARLIE = auto()
    ALL = ALICE | BOB | CHARLIE

和使用:

>>> print(Students1.ALICE | Students1.BOB)
Alice | Bob
>>> print(Students1.ALL)
Alice | Bob | Charlie

最新更新