Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create an abstract Enum class

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, based on the basic enums I defined. I'm able to get this functionality without making the class abstract.

This is the basic Flag class and an example implementation:

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"}

An example usage is:

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

Switching to TranslateableFlag(Flag, ABC) fails due to MetaClass conflicts. (I didn't understand this post - Abstract Enum Class using ABCMeta and EnumMeta, so I'm not sure if it's answering my question).

I would like get a functionality like this somehow:

@abstractclassmethod
@classmethod
    def base(cls):
        pass

Is it possible to achieve this?

like image 769
Tzahi T Avatar asked May 14 '19 13:05

Tzahi T


People also ask

Can enum class have abstract method?

Yes, you can define abstract methods in an enum declaration if and only if all enum values have custom class bodies with implementations of those methods (i.e. no concrete enum value may be lacking an implementation).

Can an enum be abstract?

Java Language Enums Enums with Abstract MethodsEnums can define abstract methods, which each enum member is required to implement. This allows for each enum member to define its own behaviour for a given operation, without having to switch on types in a method in the top-level definition.

Is enum subclass of abstract class?

enum inheritance lang. Enum is an abstract class. This is the common base class of all Java enumeration types.

Can we create instance of enum?

10) You can not create an instance of enums by using a new operator in Java because the constructor of Enum in Java can only be private and Enums constants can only be created inside Enums itself. 11) An instance of Enum in Java is created when any Enum constants are first called or referenced in code.


2 Answers

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)
        # 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):
#        ...

Result:

Traceback (most recent call last):
  ...
TypeError: cannot instantiate abstract class 'Students2' with abstract method 'base'
like image 129
martineau Avatar answered Sep 17 '22 10:09

martineau


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):
#        ...
like image 28
user443854 Avatar answered Sep 17 '22 10:09

user443854