-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Check enum members are compared by identity #5356
Comments
Thank you, I did not do that myself, but the doc is very clear about it and it definitely make sense to avoid issue with the "real value" being compared to something semantically different. |
Some additional consideration. Considering the following code: if action in {
VariableVisitConsumerAction.RETURN,
VariableVisitConsumerAction.CONSUME,
}:
return Should pylint suggest this ? if action is VariableVisitConsumerAction.RETURN or action is VariableVisitConsumerAction.CONSUME:
return |
For completeness, yes I think it would be good. Or if any(action is x for x in (VariableVisitConsumerAction.RETURN, VariableVisitConsumerAction.CONSUME)):
return ...which subjectively scales better stylewise if there are many things to compare against. But if it makes things harder, basic comparisons would be a good first step, no need to delay this to get everything covered IMHO. |
I would recommend this: if action in {
VariableVisitConsumerAction.RETURN,
VariableVisitConsumerAction.CONSUME,
}:
return It's a single set contains operation that should be faster and easier to read, especially compared to this: if any(action is x for x in (VariableVisitConsumerAction.RETURN, VariableVisitConsumerAction.CONSUME)):
return A function call ( -- from enum import Enum
from timeit import timeit
class VariableVisitConsumerAction(Enum):
RETURN = 0
CONSUME = 1
NONE = 2
action = VariableVisitConsumerAction.RETURN
ftuple = (
VariableVisitConsumerAction.RETURN,
VariableVisitConsumerAction.CONSUME,
VariableVisitConsumerAction.NONE,
)
fset = frozenset(ftuple)
def func1():
if action in fset:
return True
return False
def func2():
return any(action is x for x in ftuple)
print(timeit(func1, number=1_000_000))
print(timeit(func2, number=1_000_000))
The result isn't truly representative as it depends on |
That proposal surely is much easier on the eye, and I'm not surprised at all that it's more performant. However it arguably is not correct, >>> from enum import IntEnum
>>> class Enum1(IntEnum):
... FOO = 1
...
>>> class Enum2(IntEnum):
... BAR = 1
...
>>> Enum1.FOO in (Enum2.BAR,)
True
>>> any(Enum1.FOO is x for x in (Enum2.BAR,))
False Maybe someone can come up with a cleaner expression that would use |
It will only not work for
|
OK, I read the documentation too fast the first time (only the part >>> class Color(Enum):
... RED = 1
... BLUE = 2
...
>>> class Colored(Enum):
... RED = 1
... BLUE = 2
...
>>> Colored.RED in [Color.RED]
False
>>> Colored.RED == Color.RED
False It seems that the only issue that can happen is with I think adding a check to advise using |
To me the It makes perfect sense to compare Besides But yeah, I do think the check should apply only when one compares enum values to other enum values. And it does get perhaps somewhat convoluted with collections, at least untyped ones. |
As far as opinionatedness goes, we do have |
So if I understand you well it's comparing enum by value that is a problem, not using from enum import Enum
class Color(Enum):
RED = 1
BLUE = 2
class Colored(Enum):
RED = 1
BLUE = 2
print(Colored.RED in [Color.RED]) # False
print(Colored.RED == Color.RED) # <= No unexpected True with == even if they both == to 1
print(Colored.RED.value == Color.RED.value) # [enum-comparison-by-value]
print(Colored.RED.value in [Color.RED.value]) # [enum-comparison-by-value] Imho if you're comparing enum by value explicitly (like if you're using IntEnum) chance are it's what you want to do. |
After the discussion so far, I don't see a need for such a checker.
|
They can do so by actually comparing to ints and strings, and in that case
This is out of scope here,
As noted in earlier comments here, this does not hold universally. |
There actually wasn't an explicit example of it, so here goes. Example using an >>> from enum import IntEnum
>>> class Enum1(IntEnum):
... FOO = 1
...
>>> class Enum2(IntEnum):
... BAR = 1
...
>>> Enum1.FOO == Enum2.BAR
True
>>> Enum1.FOO is Enum2.BAR
False And custom enums may be similarly affected: >>> from enum import Enum
>>> class Enum3(str, Enum):
... QUUX = "x"
...
>>> class Enum4(str, Enum):
... BAZ = "x"
...
>>> Enum3.QUUX == Enum4.BAZ
True
>>> Enum3.QUUX is Enum4.BAZ
False |
As mentioned, that is by design. If you don't want that, just use a normal enum. |
Ok, let's stop the "as said" circle, it doesn't seem to be going anywhere. One final try/question: do you think the advice at https://docs.python.org/3/library/enum.html#comparisons is misguided, or disagree with it otherwise? They are very explicit about it (emphasis mine), it was also partially cited in the initial comment in this issue, just in case it was missed:
If yes, can I ask to submit a PR to fix it or file a bug about it? |
The doc also says just after that : Addressing the main point here:
The issue with comparing to True, False or None with equality means that a custom I'm not opposed to an optional extension, we already have an extension to prevent the use of while loop and enforce ternary operators so we're not opposed to having opinionated extensions. |
The enum classes whose members one compares (no matter how) can be defined in 3rd party code, outside one's control. |
Current problem
Enum members should be compared by identity rather than equality: https://docs.python.org/3/library/enum.html#comparisons
Desired solution
Extending
singleton-comparison
to cover this would seem to be one possible implementation.Additional context
No response
The text was updated successfully, but these errors were encountered: