1

what is the mechanism by which the following str to Enum equality comparison works?

from enum import Enum

class SE(str, Enum):
    a = 'this'
    b = 'works'

assert SE.a == 'this', "i can't believe that worked"  # passes

whereas the following fails:

class E(Enum):
    a = 'this'
    b = 'does not'

assert E.a == 'this', "i'm not surprised that didn't"  # raises

the weird thing is that str(SE.a) == 'SE.a', so i can't figure out how SE.a == 'this' works under the hood.

thanks.

to be clear: this is python 3.6

acushner
  • 9,595
  • 1
  • 34
  • 34

2 Answers2

3

In an Enum, there is no __eq__ method; two instances are equal only if they are the same object, and the EnumMeta metaclass ensures that only one object per value is created.

class E(Enum):
    a = "foo"
    b = "foo"

assert E.a is E.b

E.a == 'this' is false because E.a is 'this' is false.

An instance of SE, though, is first and foremost a str, as can be seen in its method resolution order (MRO):

>>> SE.__mro__
(<enum 'SE'>, <class 'str'>, <enum 'Enum'>, <class 'object'>)

That means SE.a == 'this', which is equivalent to SE.__eq__(SE.a, 'this') tries str.__eq__(SE.a, 'this') first, which evaluates to True.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • first, thank you! this is so close to what i'm wondering, which until now i didn't even know i was wondering: how does `str.__str__(SE.a)` resolve to `'this'` while `SE.__str__(SE.a)` resolves to `'SE.a'`... – acushner Jul 28 '20 at 19:52
2

SE subclasses both str and Enum and str is higher on the Method Resolution Order.

class SE(str, Enum):
    a = 'this'
    b = 'works'

print(SE.mro())
# [<enum 'SE'>, <class 'str'>, <enum 'Enum'>, <class 'object'>]

This means that Python first searches a method in str and only then in Enum, which means that str.__eq__ is used for the comparison.

DeepSpace
  • 78,697
  • 11
  • 109
  • 154
  • the assertion won't even occur. mixins have to be first in inheritance order, at least in python 3.6+ – acushner Jul 28 '20 at 16:19
  • @acushner you are correct, but `mro` is still the key here. See the updated answer – DeepSpace Jul 28 '20 at 16:20
  • awesome! i see what's happening! thanks for putting me on the right track. the issue is that while `str(SE.a)` does equal `'SE.a'`, `str.__str__(SE.a)` equals `this`. and thus the equality makes sense. i still don't know why that's the case though... – acushner Jul 28 '20 at 16:23