Is there a way in Python 3.2 or later to define a class whose subclasses should be created using a specific metaclass without the class itself being created using that metaclass?
An example to demonstrate what I mean: Let's say I want to create an Enum
class whose subclasses can be used to define enum types. An enum type would have a fixed number of instances, each of which has a distinct int
(or other) value. An enum type would be declared by creating a subclass of Enum
and assigning the values to attributes of that class. Through the metaclass of Enum
, the values of those attributes would be replace by instances of the new class.
The Enum
class may also define some class or instance methods or members that can be used on its subclasses or their instances.
class EnumMeta(type):
def __new__(mcs, what, bases, dict):
cls = super().__new__(mcs, what, bases, { })
cls._instances_by_value = { }
for k, v in dict.items():
if k.startswith('__') or k == 'get_by_value':
setattr(cls, k, v)
else:
instance = cls(k, v)
setattr(cls, k, instance)
cls._instances_by_value[v] = instance
return cls
class Enum(metaclass = EnumMeta):
def __init__(self, name, value):
self.name = name
self.value = value
def __repr__(self):
return '{}.{}'.format(type(self).__name__, self.name)
@classmethod
def get_by_value(cls, value):
return cls._instances_by_value[value]
An example for creating such an enum type:
class Boolean(Enum):
true = 0
false = 1
file_not_found = 2
print(Boolean.true) # Prints Boolean.true
print(Boolean.get_by_value(1)) # Prints Boolean.false
In the definition of EnumMeta.__new__
, you can see that I had to exclude get_by_value
from the processing of the metaclass. I often run into this problem when I want to process the members of a class definition in some way using a metaclass.
What is the preferred approach to exclude the base class (Enum
in the example) from processing by the metaclass (EnumMeta
in the example) while using the metaclass for all subclasses of the base class?
I don't think that excluding all members of the base class while processing the members of a subclass is the preferred approach. I don't care about the value of type(Enum)
. It can either be EnumMeta
or type
.
Your 'parent' Enum
won't have any bases, only classes derived from Enum
will have a non-empty bases
tuple. Use this to easily bail out early to create the base class with a regular __new__
call:
class EnumMeta(type):
def __new__(mcs, what, bases, dict):
if not bases:
# Enum itself
return super().__new__(mcs, what, bases, dict)
# Process subclass creation
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With