'Using objects as the values of an Enum?
I'm currently trying to create an enum/constant-based system for defining colours in Python. I want to avoid having to use strings when attempting to use/access a colour.
In an ideal world, I want to be able to do:
>>> print(Colours.BLACK)
Black
>>> print(Colours.WHITE.hex)
ffffff
So far, I have come up with an enum and object based system like the following:
from enum import Enum
class Colour:
def __init__(self, name, hex = "000000"):
self.name = name
self.hex = hex
def __str__(self):
return self.name
class Colours(Enum):
WHITE = Colour("White", "ffffff")
BLACK = Colour("Black", "000000")
print(Colours.BLACK.value)
print(Colours.WHITE.value.hex)
which prints:
Black
ffffff
However, something about assigning an object as the value of an enum feels like poor practice and I have not seen this done before elsewhere.
Is this considered bad? If so, why?
And is there a better way of achieving my desired outcome?
Edit:
I ended up going with this approach, using __hash__
as suggested by @MichealButscher and using __str__
and __repr__
to avoid using .value
.
from enum import Enum
class Colour:
def __init__(self, label, hex_value = "000000"):
self.label = label
self.hex_value = hex
def __str__(self):
return self.label
def __hash__(self):
hash(self.label)
class Colours(Enum):
WHITE = Colour("White", "ffffff")
BLACK = Colour("Black", "000000")
def __repr__(self):
return self.value
def __str__(self):
return str(self.value)
Solution 1:[1]
Everything is an object, so some object will be assigned. However, you can use just the color name and hex value in a tuple as the value, and have Colour.__init__
handle initializing each instance with the tuple.
class Colour(Enum):
WHITE = ("White", "ffffff")
BLACK = ("Black", "000000")
def __init__(self, label, hexvalue):
self.label = label
self.hexvalue = hexvalue
(Warning: certain attribute names are already in use and cannot be overridden or even assigned to, as you'll see if you try to, for example, use name
instead of label
and value
instead of hex value
.)
Solution 2:[2]
Unless you have a need for a separate color class, just combine it all into the enum:
from enum import Enum
class Colour(Enum):
WHITE = "ffffff"
BLACK = "000000"
#
def __str__(self):
return self.name
and in use:
>>> Colour.WHITE
<Colour.WHITE: 'ffffff'>
>>> str(Colour.WHITE)
'WHITE'
>>> Colour.WHITE.value
'ffffff'
@martineau, @chepner is saying the following will fail:
from enum import Enum
class Colour(Enum):
WHITE = 'White', "ffffff"
BLACK = 'black', "000000"
#
def __init__(self, name, hex):
self.name = name # uh-oh
self.hex = hex
#
def __str__(self):
return self.name
and indeed it does:
Traceback (most recent call last):
...
AttributeError: <enum 'Enum'> cannot set attribute 'name'
I think what you were saying is that the following will work:
class Example(Enum):
name = 'a name member'
value = 'a value member'
In use:
>>> list(Example)
[<Example.name: 'a name member'>, <Example.value: 'a value member'>]
>>> Example.name.name
'name'
If you weren't saying that, then you were wrong. :-/
As far as PEP 20 goes, commas are explicit.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Ethan Furman |