Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should enum instances be compared by identity or equality?

Tags:

python

enums

According to the docs, enum members are singletons.

>>> from enum import Enum >>> class Potato(Enum): ...     spud = 1234 ...     chat = 1234 ...      >>> x = 1234 >>> y = 1234 >>> x is y False >>> x = Potato.spud >>> y = Potato.chat >>> x is y True >>> x.value is y.value True 

Does that mean we should also compare them by identity, as how PEP8 suggests we should always use is/is not and never the equality operators for "singletons like None"?

I have up until now been using equality operators, and haven't noticed any issue with this to warrant such strong wording as PEP8 warns. What exactly is the drawback of using equality for enum instances, if any? Or is it just a micro-optimisation?

like image 479
wim Avatar asked Sep 15 '14 23:09

wim


People also ask

Can you use == to compare enums?

equals method uses == operator internally to check if two enum are equal. This means, You can compare Enum using both == and equals method.

Can enum variable be compared?

We can compare enum variables using the following ways. Using Enum. compareTo() method. compareTo() method compares this enum with the specified object for order.

Can I use == for enum Java?

Because there is only one instance of each enum constant, it is permissible to use the == operator in place of the equals method when comparing two object references if it is known that at least one of them refers to an enum constant. (The equals method in Enum is a final method that merely invokes super.


2 Answers

From https://docs.python.org/3/library/enum.html#module-enum:

Within an enumeration, the members can be compared by identity

In particular, from https://docs.python.org/3/library/enum.html#comparisons:

Enumeration members are compared by identity

like image 148
Dr. Jan-Philip Gehrcke Avatar answered Oct 16 '22 02:10

Dr. Jan-Philip Gehrcke


First, we can definitely rule out x.value is y.value, because those aren't singletons, those are perfectly ordinary values that you've stored in attributes.

But what about x is y?

First, I believe that, by "singletons like None", PEP 8 is specifically referring to the small, fixed set of built-in singletons that are like None in some important way. What important way? Why do you want to compare None with is?

Readability: if foo is None: reads like what it means. On the rare occasions when you want to distinguish True from other truthy values, if spam is True: reads better than if spam == True:, as well as making it more obvious that this isn't just a frivolous == True used by someone improperly following a C++ coding standard in Python. That might apply in foo is Potato.spud, but not so much in x is y.

Use as a sentinel: None is used to mean "value missing" or "search failed" or similar cases. It shouldn't be used in cases where None itself can be a value, of course. And if someone creates a class whose instances compare equal to None, it's possible to run into that problem without realizing it. is None protects against that. This is even more of a problem with True and False (again, on those rare occasions when you want to distinguish them), since 1 == True and 0 == False. This reason doesn't seem to apply here—if 1 == Potato.spud, that's only because you intentionally chose to use an IntEnum instead of an Enum, in which case that's exactly what you want…

(Quasi-)keyword status: None and friends have gradually migrated from perfectly normal builtin to keyword over the years. Not only is the default value of the symbol None always going to be the singleton, the only possible value is that singleton. This means that an optimizer, static linter, etc. can make an assumption about what None means in your code, in a way that it can't for anything defined at runtime. Again, this reason doesn't seem to apply.

Performance: This really is not a consideration at all. It might be faster, on some implementations, to compare with is than with ==, but this is incredibly unlikely to ever make a difference in real code (or, if it does, that real code probably needs a higher-level optimization, like turning a list into a set…).

So, what's the conclusion?

Well, it's hard to get away from an opinion here, but I think it's reasonable to say that:

  • if devo is Potato.spud: is reasonable if it makes things more readable, but as long as you're consistent within a code base, I don't think anyone will complain either way.
  • if x is y:, even when they're both known to be Potato objects, is not reasonable.
  • if x.value is Potato.spud.value is not reasonable.
like image 23
abarnert Avatar answered Oct 16 '22 02:10

abarnert