Dynamically Subclass An Enum Base Class
Solution 1:
This line:
C1 = enum.EnumMeta('C1', (), dict(a = 0))
fails with exactly the same error message. The __new__
method of EnumMeta
expects an instance of enum._EnumDict
as its last argument. _EnumDict
is a subclass of dict
and provides an instance variable named _member_names
, which of course a regular dict
doesn't have. When you go through the standard mechanism of enum creation, this all happens correctly behind the scenes. That's why your other example works just fine.
This line:
C1 = enum.EnumMeta('C1', (), enum._EnumDict())
runs with no error. Unfortunately, the constructor of _EnumDict is defined as taking no arguments, so you can't initialize it with keywords as you apparently want to do.
In the implementation of enum that's backported to Python3.3, the following block of code appears in the constructor of EnumMeta
. You could do something similar in your LineMakerMeta class:
def__new__(metacls, cls, bases, classdict):
iftype(classdict) isdict:
original_dict = classdict
classdict = _EnumDict()
for k, v in original_dict.items():
classdict[k] = v
In the official implementation, in Python3.5, the if statement and the subsequent block of code is gone for some reason. Therefore classdict
must be an honest-to-god _EnumDict
, and I don't see why this was done. In any case the implementation of Enum
is extremely complicated and handles a lot of corner cases.
I realize this is not a cut-and-dried answer to your question but I hope it will point you to a solution.
Solution 2:
Create your LineMakerBase
class, and then use it like so:
C1 = LineMakerBase('C1', dict(a=0))
The metaclass was not meant to be used the way you are trying to use it. Check out this answer for advice on when metaclass subclasses are needed.
Some suggestions for your code:
the double try/except in format
seems clearer as:
formemberin cls:
if memberin kwargs:
value= kwargs[member]
elif member.name in kwargs:
value= kwargs[member.name]
else:
value= member.default
this code:
# compare by member values because member could be an aliasmembers = list(type(member))
- would be clearer with
list(member.__class__)
- has a false comment:
list
ing anEnum
class will never include the aliases (unless you have overridden that part ofEnumMeta
)
instead of the complicated __len__
code you have now, and as long as you are subclassing EnumMeta
you should extend __new__
to automatically calculate the lengths once:
# untesteddef__new__(metacls, cls, bases, clsdict):
# let the main EnumMeta code do the heavy lifting
enum_cls = super(LineMakerMeta, metacls).__new__(cls, bases, clsdict)
# go through the members and calculate the lengths
canonical_members = [
member
for name, member in enum_cls.__members__.items()
if name == member.name
]
last_member = Nonefor next_member in canonical_members:
next_member.length = 0if last_member isnotNone:
last_member.length = next_member.start - last_member.start
Solution 3:
The simplest way to create Enum
subclasses on the fly is using Enum
itself:
>>>from enum import Enum>>>MyEnum = Enum('MyEnum', {'a': 0})>>>MyEnum
<enum 'MyEnum'>
>>>MyEnum.a
<MyEnum.a: 0>
>>>type(MyEnum)
<class 'enum.EnumMeta'>
As for your custom methods, it might be simpler if you used regular functions, precisely because Enum
implementation is so special.
Post a Comment for "Dynamically Subclass An Enum Base Class"