Reputation: 2988
The docs state that it is possible to make an enumeration class whose members are exactly of a certain type by adding that type as a mixin to the enum class:
While :class:
IntEnum
is part of the :mod:enum
module, it would be very simple to implement independently::class IntEnum(int, Enum): pass
This demonstrates how similar derived enumerations can be defined; for example a :class:
StrEnum
that mixes in :class:str
instead of :class:int
.Some rules:
- When subclassing :class:
Enum
, mix-in types must appear before :class:Enum
itself in the sequence of bases, as in the :class:IntEnum
example above.- While :class:
Enum
can have members of any type, once you mix in an additional type, all the members must have values of that type, e.g. :class:int
above. This restriction does not apply to mix-ins which only add methods and don't specify another type.- When another data type is mixed in, the :attr:
value
attribute is not the same as the enum member itself, although it is equivalent and will compare equal.- %-style formatting:
%s
and%r
call the :class:Enum
class's :meth:__str__
and :meth:__repr__
respectively; other codes (such as%i
or%h
for IntEnum) treat the enum member as its mixed-in type.- :ref:
Formatted string literals <f-strings>
, :meth:str.format
, and :func:format
will use the mixed-in type's :meth:__format__
unless :meth:__str__
or :meth:__format__
is overridden in the subclass, in which case the overridden methods or :class:Enum
methods will be used. Use the !s and !r format codes to force usage of the :class:Enum
class's :meth:__str__
and :meth:__repr__
methods.
I was trying to make an enum whose members were types (objects of type type
), but since type
is a special class (it is a metaclass more than a class), I'm getting a bunch of errors like argument mismatch for mro
, __new__
, __init__
, etc:
import enum
class A: pass
class B: pass
class C(B): pass
class MyTypes(type, enum.Enum):
SPAM = A
EGGS = C
Example of error:
Traceback (most recent call last):
File "<file>", line 10, in <module>
class MyTypes(type, enum.Enum):
File "<file>", line 182, in __new__
dynamic_attributes = {k for c in enum_class.mro()
TypeError: descriptor 'mro' of 'type' object needs an argument
Is there a way to achieve this?
I realise this might be an XY problem, so I'll explain my concrete example. In a card game I'm developing, I have a Card
class hierarchy; each of the subclasses represents a different card type. However, the mapping between card types in the game and subclasses of Card
might not be 1-to-1; for example there might be some intermediate abstract classes that serve as an implementation help but that do not represent an actual card type.
So I want to make an enumeration that contains all of the game card types, and I want the members to be the actual concrete subclasses implementing those types, so that I can easily interoperate enum members and the actual classes: the idea is to access class attributes, instantiate them, etc without having to use .value
every time (as in some cases I might have to work with something that is either a member of the enum or the class itself).
Upvotes: 3
Views: 5384
Reputation: 69021
Short answer: no, it is not currently possible to mix in type
to an enum.
The purpose behind mixing in data types is two-fold:
class Number(int, Enum):
ONE = 1
TWO = 2
THREE = 'three'
# ValueError: invalid literal for int() with base 10: 'three'
class Number(int, Enum):
ONE = 1
TWO = 2
Number.ONE + 2
# 3
As you noted, type
is not a data type -- it is a metaclass. It would need special support to work. What is your use-case?
Disclosure: I am the author of the Python stdlib Enum
, the enum34
backport, and the Advanced Enumeration (aenum
) library.
Upvotes: 5