问题
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?
回答1:
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'
回答2:
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
来源:https://stackoverflow.com/questions/56131308/create-an-abstract-enum-class