Search code examples
pythonenumsaliasdefaultdict

Python enum.Enum creates alias instead of new value


I just noticed a very weird behavior of Enum+defaultdict in pyhton. I define an enum like this, which collects some default dictionaries:

from enum import Enum
from collections import defaultdict

class A(Enum):
     a = defaultdict(lambda: 1)
     b = defaultdict(lambda: 2)

Then, when I look at what's inside A:

In [11]: A.a
Out[11]: <A.a: defaultdict(<function A.<lambda> at 0x7f773f03b510>, {})>

In [12]: A.b
Out[12]: <A.a: defaultdict(<function A.<lambda> at 0x7f773f03b510>, {})>

So A.b is just an alias to A.a. This looks a lot like a bug to me, even if there probably is some good reason behind. Any idea?

Addendum

Since it popped up in the comments, a note on why one would like to put a dict in an Enum. I find that it is very often useful to group constants into namespaces, so that one may access values as A.a.constant1, A.b.constant1, etc. At the moment, trying to do so with Enums raises an AttributeError. Of course, one could do something like:

class A:
    class a(Enum):
        constant1 = 1
    class b(Enum):
        constant1 = 2

However, this does not leverage on the capabilities of Enum (iteration, access via __getitem__ etc).


Solution

  • If you give two enum members equal values, enum will produce one enum member with two aliases rather than two distinct members.

    Defaultdicts inherit equality comparison from dict. That means the default factory isn't part of == comparisons. Your enum members have equal values, so they get merged.

    Using mutable objects as enum values is usually a bad idea. They tend to break enum design assumptions like "value equality doesn't change". Consider attaching these defaultdicts to your enum members in some other way.