I found a very strange behavior in the Enum class in Python. So the enumerated type is simple:
from enum import Enum
Analysis = Enum('Analysis', 'static dynamic')
So I use this enumerated type in for step objects so that they store it in the attribute analysis, as follows:
class Step:
def __init__(self):
self.analysis = None
self.bcs = []
Very simple so far, so when I have a few of these steps in a list, then I try to see the enumerated type and it has been assigned correctly. But they are not equal:
# loop over steps
for s, step in enumerate(kwargs['steps']):
print(kwargs)
print(step)
print(step.analysis)
print("test for equality: ",(step.analysis == Analysis.static))
quit()
which prints
{'mesh': <fem.mesh.mesh.Mesh object at 0x10614d438>,
'steps': [<hybrida.fem.step.Step object at 0x10614d278>,
<hybrida.fem.step.Step object at 0x10616a710>,
<hybrida.fem.step.Step object at 0x10616a390>]}
Step:
analysis: Analysis.static
bcs: [<hybrida.fem.conditions.dirichlet.Dirichlet object at 0x10616a0f0>,
<hybrida.fem.conditions.dirichlet.Dirichlet object at 0x10616a320>,
<hybrida.fem.conditions.dirichlet.Dirichlet object at 0x10616a3c8>,
<hybrida.fem.conditions.dirichlet.Dirichlet object at 0x10616a470>,
<hybrida.fem.conditions.dirichlet.Dirichlet object at 0x10616a518>,
<hybrida.fem.conditions.dirichlet.Dirichlet object at 0x10616a5c0>,
<hybrida.fem.conditions.dirichlet.Dirichlet object at 0x10616a668>]
Analysis.static
test for equality: False
This is not correct, but I have no ideas on how to debug this.
UPDATE
Following the suggestion by @martineau, I created an IntEnum
instead and that solved my problem. Yet, I don't understand why the normal Enum
doesn't work.
It's hard to voice what your actual needs are to a potential partner for fear that they may write you off. Transparency requires over communication. If your communication skills are already not that great, ENM is going to be more of a headache than an opportunity to be your most authentic self.
Enum is a class in python for creating enumerations, which are a set of symbolic names (members) bound to unique, constant values. The members of an enumeration can be compared by these symbolic anmes, and the enumeration itself can be iterated over.
Syntax : enum.auto() Automatically assign the integer value to the values of enum class attributes. Example #1 : In this example we can see that by using enum. auto() method, we are able to assign the numerical values automatically to the class attributes by using this method.
Enums can't have multiple value per name.
Enums can be displayed as string or repr. 2. Enums can be checked for their types using type (). 3. “ name ” keyword is used to display the name of the enum member. 4. Enumerations are iterable. They can be iterated using loops
Changed in version 3.11. Enum members are instances of their enum class, and are normally accessed as EnumClass.member. In Python versions 3.5 to 3.10 you could access members from other members – this practice was discouraged, and in 3.11 Enum returns to not allowing it:
The __new__ () method, if defined, is used during creation of the Enum members; it is then replaced by Enum’s __new__ () which is used after class creation for lookup of existing members. An ordered enumeration that is not based on IntEnum and so maintains the normal Enum invariants (such as not being comparable to other enumerations):
While Enum can have members of any type, once you mix in an additional type, all the members must have values of that type, e.g. int above. This restriction does not apply to mix-ins which only add methods and don’t specify another data type such as int or str.
In the comments, you say:
The input file contains many steps, and every time I add a new step I have to set up the analysis type
If I understand you correctly, you're saying that you create a new Enum
object each time you add a new step. This may be why you're seeing your "bug". The values of two different Enum
objects, despite having the same name and order, do not necessarily compare as equal. For example:
import enum
Analysis1 = enum.Enum("Analysis", "static dynamic")
Analysis2 = enum.Enum("Analysis", "static dynamic")
But:
>>> Analysis1.static == Analysis2.static
False
This happens because the equality operator is not defined for Enum
objects, as far as I can tell, so the default behavior of checking id
s is used.
As @martineau suggests in the comments, one way of avoiding this issue is to instead use the IntEnum
type, which subclasses int
, and therefore defines the equality operator in terms of the value of the Enum
, not the id
:
import enum
Analysis1 = enum.IntEnum("Analysis", "static dynamic")
Analysis2 = enum.IntEnum("Analysis", "static dynamic")
Then:
>>> Analysis1.static == Analysis2.static
True
Enum
and IntEnum
?It may seem at first glance that IntEnum
is always what we want. So what's the point of Enum
?
Suppose you want to enumerate two sets of items, say, fruits and colors. Now, "orange" is both a fruit, and a color. So we write:
Fruits = enum.IntEnum("Fruits", "orange apple lemon")
Colors = enum.IntEnum("Colors", "orange red blue")
But now:
>>> Fruits.orange == Colors.orange
True
But, philosophically speaking, "orange" (the fruit) is not the same as "orange" (the color)! Shouldn't we be able to distinguish the two? Here, the subclassing of int
by IntEnum
works against us, as both Fruits.orange
and Colors.orange
equate to 1
. Of course, as we saw above, comparison of Enum
s compares id
s, not values. Since Fruits.orange
and Colors.orange
are unique objects, they do not compare as equal:
Fruits = enum.Enum("Fruits", "orange apple lemon")
Colors = enum.Enum("Colors", "orange red blue")
So that:
>>> Fruits.orange == Colors.orange
False
and we no longer live in a world where some colors are things that you can find in the produce section of your local grocery store.
In case anyone else finds themselves here after us, we experienced the same issue. We were able to trace it back to an unintentional mixing of absolute and relative imports, in a manner similar to the situation described below.
# File: package/module/analysis_types.py
Analysis = enum.Enum("Analysis", "static dynamic")
# File: package/module/static_thing.py
from .analysis_types import Analysis
class StaticThing:
...
analysis = Analysis.static
...
# File: package/module/static_thing_test.py
from package.module.static_thing import StaticThing
from .analysis_types import Analysis
# This throws an AssertionError because as
# id(StaticThing.analysis) != id(Analysis.static)
assert StaticThing.analysis == Analysis.static
Expected behavior was restored with the following changes:
# File: package/module/static_thing_test.py
from .static_thing import StaticThing
from .analysis_types import Analysis
# This does NOT throw an AssertionError because as
# id(StaticThing.analysis) == id(Analysis.static)
assert StaticThing.analysis == Analysis.static
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