4

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') 
6
  • 1
    no, there isn't without fundamentally changing how enum works. What is your use-case for enum here? why not just use the classes directly? In any case, value isn't a method, it is simply the attribute on the enum object which holds the value you gave to that enum member. Commented Jul 23, 2019 at 19:56
  • @chepner I'm not sure the OP is trying to dynamically generate a class. Commented Jul 23, 2019 at 19:58
  • @chepner but to me it seems like the issue is that the OP wants to be able to call Color.HEX() to instantiate an instance, rather than Color.HEX.value(). Honestly, to do what you're trying to do i would just use the class definition statement and simply del the names, if that was bothering me. Of course, I simply think that this isn't a use-case for enum Commented Jul 23, 2019 at 20:00
  • Actually, I forgot that since a class statement is just a fancy assignment statement, you can just nest the definitions of Hex and Rgb directly in Color, so that eliminates the need for the type hackery. True, it doesn't eliminate the issue of having to use value to get back the actual type instance. Commented Jul 23, 2019 at 20:03
  • I ran your code, unfortunately IDLE's subprocess did not make connection Commented Jul 23, 2019 at 20:03

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

5 Comments

This is cool, but then why bring Enum into the picture at all? Just use Color(object) and give it class attributes?
That's a question you could ask about just about any use of Enum.
There are good use-cases for using Enum -- the OP's is not one of them.
I'm not a fan of nesting the classes inside. cause it makes them unavailable outside. doing this approach i would rather just have the enum point at the outer class definitions. not having them defined inside.
You weren't using them outside the class in the first place; the whole point of the question was how to write Color.HEX(...) instead of Hex(...).
1

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 aenum1 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.

1 Comment

I came to the realization that my way of using the Enum here isn't very pythonic. i'm used to languages that doesn't have inheritance. were grouping types in an Enum is very useful. but in this case it doesn't offer any benefits apart from the pathing. which can be achived by nesting modules in folders using normal inheritance.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.