2

Is it possible to initialize a static/class dictionary in an inner enum class like other variables?

# file Outer.py
from enum import Enum
class Outer:
    def callInner(self):
        all_a = Outer.Inner.ALL
        print(all_a) # prints Inner.ALL instead of the list
        all_b = Outer.Inner.ALL[:] # TypeError Inner is not subscriptable

        for e in all_a: #Inner is not iterable
            print(e.to_string())

    class Inner(Enum):
        A = 1
        B = 2
        ALL = [A,B]
        NAMES = {A : "some_name_other_than_enum_a_name",
                 B : "some_name_other_than_enum_b_name"}

        def to_string(self):
             return Outer.Inner.NAMES[self.value]

if __name__ == '__main__':
    o = Outer()
    o.callInner()

The class Outer is the module with all the logic. The class Inner is an enum which does contain the enum key-value pairs A=1and B=2 as well as a list of all possible enums (or any interesting subset thereof). The idea is to have a list for quick reference and iteration/enumeration over those while to_string could be an arbritray method containing any logic. The name lookup is just a simplification to make the problem clear.

Kevin Streicher
  • 484
  • 1
  • 8
  • 25
  • I don't understand what you're asking. This code wouldn't print anything at all, because you never call `to_string()`. Exactly *what* are you running, and what do you see? – Daniel Roseman Dec 25 '15 at 19:34
  • What about `self.NAMES`? I don't remember everything that the enum metaclass does, but it seems to me that `self.NAMES` should work just fine . . . (Also, it seems like a `classmethod` _might_ be more appropriate here) – mgilson Dec 25 '15 at 19:36
  • Why is `Inner` inheriting from a `module`/`package`? The code throws `class Inner(enum): TypeError: module.__init__() takes at most 2 arguments (3 given)` – Pynchia Dec 25 '15 at 20:29
  • @Pynchia Because he users the`enum` module as his base "class". `enum.Enum` is the correct base class. – pppery Dec 25 '15 at 21:08
  • I corrected the import and typo of `enum.Enum` and tried to improve the question to clearly show what the problem is. The calls from __main__ down to innerCall should now be visible. The list and dictionary seem uninitialized and inaccessible. I also tried to make clear why the to_string method is not a class method. I have added the thrown errors for the different variations I tried. @ppperry thank you for pointing the enum error out. The question looks similar to the linked one, but isn't the ALL list and NAMES dictionary exactly what they do with "G" there? Why is there a TypeError then? – Kevin Streicher Dec 26 '15 at 13:06
  • You want them to, but you haven't set them up the same way `G` is set up in the linked answer. I edited my answer to provide more details. – Ethan Furman Dec 26 '15 at 17:29

1 Answers1

4

The issue here is not that you have an inner class, but that the inner class is an Enum; however, it is possible to have non-member attributes as part of an Enum class -- see this question and answer for the details.

To summarize, you need to make ALL and NAMES with some kind of descriptor to avoid having them transformed into enum members:

# inherit from `object` if using Python 2
class classattribute:   # was called Constant in the linked answer
    def __init__(self, value):
        self.value = value
    def __get__(self, *args):
        return self.value
    def __set__(self, _, value):
        self.value = value
    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, self.value)

and then in your Inner:

    ALL = classattribute([A, B])
    NAMES = classattribute({
            A : "some_name_other_than_enum_a_name",
            B : "some_name_other_than_enum_b_name",
            })

This will avoid the errors you are getting in your callInner() method, but will add a new one at the print(e.to_string()) line:

AttributeError: 'int' object has no attribute 'to_string'

The reason for this is that constructing an Enum is a two-part process:

  • gather all the definitions:

    { 'A':1, 'B':2, 'ALL':classattribute([A, B]), 'NAMES':classattribute({'A':..., 'B':...}), 'to_string':method(...), }

  • transform anything not a __dunder__, _sunder_, nor descriptor into an enum member:

    • A -> <Inner.A: 1>
    • B -> <Inner.B: 2>

What this means is that when ALL and NAMES were being created, A and B were still ints, and ints don't have a to_string method. The easy way around that is to retrieve the enum member before trying to access those methods: self.Inner(e).to_string().

To pull it all together, here is what your code should look like:

# file Outer.py

from enum import Enum

class classattribute:
    def __init__(self, value):
        self.value = value
    def __get__(self, *args):
        return self.value
    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, self.value)

class Outer:
    def callInner(self):
        all_a = Outer.Inner.ALL
        print(all_a)
        all_b = Outer.Inner.ALL[:]
        for e in all_a: #Inner is not iterable
            print(self.Inner(e).to_string())

    class Inner(Enum):
        A = 1
        B = 2
        ALL = classattribute([A,B])
        NAMES = classattribute(
                {A : "some_name_other_than_enum_a_name",
                 B : "some_name_other_than_enum_b_name"}
                )
        def to_string(self):
             return Outer.Inner.NAMES[self.value]

if __name__ == '__main__':
    o = Outer()
    o.callInner()

and when run, this is what you get:

[1, 2]
some_name_other_than_enum_a_name
some_name_other_than_enum_b_name
Community
  • 1
  • 1
Ethan Furman
  • 63,992
  • 20
  • 159
  • 237
  • I have tried to edit the question to make it more clear which errors occur and what errors occur. Sorry that I was not clear with the import and inheritance of enum.Enum, now I have fixed that. I have added also some more details about the complete code workflow from `__main__` down to `Inner` to make it clear where and when I try to access the enum constants. – Kevin Streicher Dec 26 '15 at 13:13
  • Thank you for this in depth explanation! – Kevin Streicher Dec 26 '15 at 17:38