On Sun, Apr 24, 2016 at 1:20 AM, Ethan Furman <et...@stoneleaf.us> wrote: > On 04/23/2016 06:29 PM, Ian Kelly wrote: > >> Python enums are great. Sadly, they're still not quite as awesome as Java >> enums. > > > What fun things can Java enums do?
Everything that Python enums can do, plus: * You can override methods of individual values, not just the class as a whole. Good for implementing the strategy pattern, or for defining a default method implementation that one or two values do differently. In Python you can emulate the same thing by adding the method directly to the instance dict of the enum value, so this isn't really all that much of a difference. * Java doesn't have the hokey notion of enum instances being distinct from their "value". The individual enum members *are* the values. Whereas in Python an enum member is an awkward class instance that contains a value of some other type. Python tries to get away from the C-like notion that enums are ints by making the enum members non-comparable, but then gives us IntEnum as a way to work around it if we really want to. Since Java enums don't depend on any other type for their values, there's nothing inviting the user to treat enums as ints in the first place. * As a consequence of the above, Java doesn't conflate enum values with their parameters. The Python enum docs give us this interesting example of an enum that takes arguments from its declaration: >>> class Planet(Enum): ... MERCURY = (3.303e+23, 2.4397e6) ... VENUS = (4.869e+24, 6.0518e6) ... EARTH = (5.976e+24, 6.37814e6) ... MARS = (6.421e+23, 3.3972e6) ... JUPITER = (1.9e+27, 7.1492e7) ... SATURN = (5.688e+26, 6.0268e7) ... URANUS = (8.686e+25, 2.5559e7) ... NEPTUNE = (1.024e+26, 2.4746e7) ... def __init__(self, mass, radius): ... self.mass = mass # in kilograms ... self.radius = radius # in meters ... @property ... def surface_gravity(self): ... # universal gravitational constant (m3 kg-1 s-2) ... G = 6.67300E-11 ... return G * self.mass / (self.radius * self.radius) ... >>> Planet.EARTH.value (5.976e+24, 6378140.0) >>> Planet.EARTH.surface_gravity 9.802652743337129 This is incredibly useful, but it has a flaw: the value of each member of the enum is just the tuple of its arguments. Suppose we added a value for COUNTER_EARTH describing a hypothetical planet with the same mass and radius existing on the other side of the sun. [1] Then: >>> Planet.EARTH is Planet.COUNTER_EARTH True Because they have the same "value", instead of creating a separate member, COUNTER_EARTH gets defined as an alias for EARTH. To work around this, one would have to add a third argument to the above to pass in an additional value for the sole purpose of distinguishing (or else adapt the AutoNumber recipe to work with this example). This example is a bit contrived since it's generally not likely to come up with floats, but it can easily arise (and in my experience frequently does) when the arguments are of more discrete types. It's notable that the Java enum docs feature this very same example but without this weakness. [2] * Speaking of AutoNumber, since Java enums don't have the instance/value distinction, they effectively do this implicitly, only without generating a bunch of ints that are entirely irrelevant to your enum type. With Python enums you have to follow a somewhat arcane recipe to avoid specifying values, which just generates some values and then hides them away. And it also breaks the Enum alias feature: >>> class Color(AutoNumber): ... red = default = () # not an alias! ... blue = () ... >>> Color.red is Color.default False Anyroad, I think that covers all my beefs with the way enums are implemented in Python. Despite the above, they're a great feature, and I use them and appreciate that we have them. [1] https://en.wikipedia.org/wiki/Counter-Earth [2] https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html -- https://mail.python.org/mailman/listinfo/python-list