Create an abstract Enum class

后端 未结 3 614
挽巷
挽巷 2020-12-11 02:13

I\'m trying to create an abstract enum (Flag actually) with an abstract method. My final goal is to be able to create a string representation of compound enums,

相关标签:
3条回答
  • 2020-12-11 02:34

    Here is a fix of the accepted answer for python 3.8. The only change is to the ABCEnumMeta. The rest is copy-pasted from the original answer to provide a runnable example. Also tested on 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):
    #        ...
    
    0 讨论(0)
  • 2020-12-11 02:37

    If the goal is simply to change the __str__ output of Students1, you do not need to use abstract classes:

    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
    

    and in use:

    >>> print(Students1.ALICE | Students1.BOB)
    Alice | Bob
    
    >>> print(Students1.ALL)
    Alice | Bob | Charlie
    
    0 讨论(0)
  • 2020-12-11 02:51

    Here's how to adapt the accepted answer to the question Abstract Enum Class using ABCMeta and EnumMeta to create the kind of abstract Enum class you want:

    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)
            try:  # Handle existence of undefined abstract methods.
                absmethods = list(abstract_enum_cls.__abstractmethods__)
                absmethods_str = ', '.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} {absmethods_str}")
            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"}
    
    
    class Students2(TranslateableFlag):
        ALICE = auto()
        BOB = auto()
        CHARLIE = auto()
        ALL = ALICE | BOB | CHARLIE
    
    # Abstract method not defined - should raise TypeError.
    #    @classmethod
    #    def base(cls):
    #        ...
    

    Result:

    Traceback (most recent call last):
      ...
    TypeError: cannot instantiate abstract class 'TranslateableFlag' with abstract method 'base'
    
    0 讨论(0)
提交回复
热议问题