Should enumeration instances be compared using identity or equality?

According to the docs, enumerating members are single points .

>>> 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 this mean that we should also compare them by identity, because, as PEP8 shows, we should always use is / is and are not equality operators for "single singles like None"?

I have used equality operators so far and have not noticed any problems with this to justify such a strong wording as PEP8 warns. What is the disadvantage of using equality for enum instances, if any? Or is it just micro-optimization?

+7
python enums
source share
3 answers

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

Within the enumeration, members can be compared with an identifier

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

Listing items are compared by identifier.

+3
source share

First, we can definitely exclude x.value is y.value , because these are not singletones, these are completely ordinary values ​​that you saved in the attributes.

But what about x is y ?

First of all, I think that “singleton like None” PEP 8 refers specifically to a small fixed set of built-in singletones that are somehow similar to None . What is the important way? Why do you want to compare None with is ?

Readability: if foo is None: reads like what it means. In rare cases when you want to distinguish True from other right values, if spam is True: reads better than if spam == True: and also makes it more obvious that it is not just frivolous == True used by someone inappropriate way C ++ standard in Python. This can be used in foo is Potato.spud , but not so much in x is y .

Use as a sentinel: None used to indicate "value missing" or "search not completed" or similar cases. It should not be used in cases where None itself can be a value, of course. And if someone creates a class whose instances are compared to None , he can deal with this problem without realizing it. is None protects against this. This is an even bigger problem with True and False (again, in those rare cases when you want to distinguish them), since 1 == True and 0 == False . This reason does not seem to apply here if 1 == Potato.spud , which is only because you intentionally decided to use IntEnum instead of Enum , in which case exactly what you want ...

Keyword Status

(Quasi-): None , and friends gradually switched from a perfectly normal inline to a keyword over the years. Not only will the default value of the None symbol be always single, the only possible value is singleton. This means optimizer, static linter, etc. They may make an assumption about what None means in your code, so that it cannot for anything specific at runtime. Again, this reason does not seem to apply.

Performance: This is really not a consideration at all. In some implementations, it may be faster to compare with is than with == , but it is incredibly unlikely to ever change the real code (or, if that happens, this real code probably needs a higher level of optimization, such as list conversion into the set ...).

So what is the conclusion?

Well, it's hard for me to get away from the 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 agree on the code base, I don't think anyone will complain anyway.
  • if x is y: even if they are both known as Potato objects, are not reasonable.
  • if x.value is Potato.spud.value not reasonable.
+3
source share

PEP 8 says:

Comparison with single-point None types should always be done using is or is not , never executed equality operators.

I disagree with abarnert: the reason for this is not that they are built-in or special in any way. This is because in these cases you care about the availability of the object, and not about what it looks like.

When using is None , for example, you care about whether it was None as specified by you, and not the other None that was transferred. It may be difficult to (after all, there is only one None ), but sometimes it matters.

Take for example:

 no_argument = object() def foo(x=no_argument): if x OP no_argument: ... ... 

If OP is , this is a completely idiomatic code. If it == , it is not.

For the same reason, you should make a decision like this:

  • If you want the duck type to be equal, for example with IntEnum or Enum, which you want to subclass and rewrite (for example, when you have complex enumeration types with methods and other additions), it makes sense to use == .

  • When you use enumerations as silent guards, use is .

+2
source share

All Articles