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?
equals method uses == operator internally to check if two enum are equal. This means, You can compare Enum using both == and equals method.
We can compare enum variables using the following ways. Using Enum. compareTo() method. compareTo() method compares this enum with the specified object for order.
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.
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
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.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With