Skip to content Skip to sidebar Skip to footer

Dynamically Subclass An Enum Base Class

I've set up a metaclass and base class pair for creating the line specifications of several different file types I have to parse. I have decided to go with using enumerations beca

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))
  1. would be clearer with list(member.__class__)
  2. has a false comment: listing an Enum class will never include the aliases (unless you have overridden that part of EnumMeta)

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 Enumitself:

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