Reputation: 73
I recently found out you can use enums in Python, but I'm not happy with how my code is working and I'm wondering if there is a cleaner way to implement it.
from enum import Enum
class Hex:
def __init__(self, hex_code: str):
self.code = hex_code
class Rgb:
def __init__(self, R: int, G: int, B: int):
self.r = R
self.g = G
self.b = B
class Color(Enum):
HEX = Hex
RGB = Rgb
def main():
hex_color = Color.HEX.value('#00FF00')
rgb_color = Color.RGB.value(255, 255, 255)
if __name__ == "__main__":
main()
In this example I have to instantiate by calling the .value()
enum method. but when you instantiate a class normally, all you do is Class(value)
. Would it be possible to implement something similar to enum variants that holds a class?
For example:
Color.HEX('#00FF00')
# Instead of:
Color.HEX.value('#00FF00')
Upvotes: 4
Views: 2705
Reputation: 69288
I see nothing in your question that requires, or is benefited by, the use of Enum
.
Check this for guidelines on using Enum
.
Enum
does offer easy membership testing, so you could do:
hex_value is rgb_value
or
hex_value in Color
Using the aenum
1 library, your code would look like this:
from aenum import Enum, extend_enum
class Color(Enum):
#
def __new__(cls, value):
member = object.__new__(cls)
member._value_ = value
member.hex = hex(value)[2:]
r, value = divmod(value, 1 << 16)
g, value = divmod(value, 1 << 8)
b = value
member.red = r
member.green = g
member.blue = b
member.rgb = r, g, b
return member
#
@classmethod
def _missing_(cls, value):
# r, g, b = value
name = 'rgb:%r' % (value, )
# name = 'rgb:%r' % ((r << 16) + (g << 8) + b, )
extend_enum(cls, name, value)
return cls[name]
#
@classmethod
def from_hex(cls, value):
# on leading #
red = int(value[:2], 16)
green = int(value[2:4], 16)
blue = int(value[4:], 16)
value = (red << 16) + (green << 8) + blue
return cls(value)
#
@classmethod
def from_rgb(cls, red, green, blue):
value = (red << 16) + (green << 8) + blue
return cls(value)
#
RED = 255 << 16
GREEN = 255 << 8
BLUE = 255
and in use:
>>> list(Color)
[<Color.RED: 16711680>, <Color.GREEN: 65280>, <Color.BLUE: 255>]
>>> Color.from_rgb(255, 0, 0)
<Color.RED: 16711680>
>>> Color.from_hex('00FF00')
<Color.GREEN: 65280>
>>> Color.from_hex('15A97F')
<Color.rgb:1419647: 1419647>
>>> Color.from_rgb(21, 169, 127)
<Color.rgb:1419647: 1419647>
>>> Color.from_hex('15A97F') is Color.from_rgb(21, 169, 127)
True
You can, of course, change the details of the repr()
, the stored attributes (red
, green
, blue
, hex
, rgb
, etc).
1 Disclosure: I am the author of the Python stdlib Enum
, the enum34
backport, and the Advanced Enumeration (aenum
) library.
Upvotes: 1
Reputation: 532418
HEX
and RGB
are not classes; they are instances of Color
. (enum
uses a metaclass that abuses the class
statement quite a bit.) You use the value
attribute of those instances to get the value you "assigned" to those names.
In order to make Color.HEX('#00ff00')
return an instance of the class Hex
, you need to define Color.__call__
. (As an aside, note that you can simply define the two classes inside the class
statement that defines Color
, rather than defining them externally. A class
statement is just a fancy assignment statement at heart.)
from enum import Enum
class Color(Enum):
class HEX:
def __init__(self, hex_code: str):
self.code = hex_code
class RGB:
def __init__(self, R: int, G: int, B: int):
self.r = R
self.g = G
self.b = B
def __call__(self, *args):
return self.value(*args)
Then
>>> Color.HEX('#00ff00')
<__main__.Color.HEX object at 0x108740f28>
There's no inherited value of __call__
being overriden, so there's no immediate need to use anything like super().__call__(*args)
in its definition. That might change if you think you'll need to support multiple inheritance, but given the use of a custom metaclass by Enum
, I'm going to declare handling that beyond the scope of this question.
Upvotes: 3