Python 3.5 内部 class(enum) 中 static/class lists/dictionaries 的初始化

Initialization of static/class lists/dictionaries in inner class(enum) in Python 3.5

是否可以像其他变量一样在内部枚举 class 中初始化 static/class 字典?

# 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()

class Outer 是包含所有逻辑的模块。 class Inner 是一个枚举,它确实包含枚举键值对 A=1B=2 以及所有可能的枚举列表(或其任何有趣的子集) .这个想法是有一个列表供快速参考和 iteration/enumeration 在那些之上,而 to_string 可以是包含任何逻辑的任意方法。名称查找只是为了使问题更清楚的简化。

这里的问题不是你有一个内部 class,而是内部 class 是一个 Enum;但是,可以将非成员属性作为 Enum class 的一部分——有关详细信息,请参阅 this question and answer

总而言之,您需要使用某种描述符来制作 ALLNAMES,以避免将它们转换为枚举成员:

# 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)

然后在你的 Inner:

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

这将避免您在 callInner() 方法中遇到的错误,但会在 print(e.to_string()) 行添加一个新错误:

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

这样做的原因是构建 Enum 是一个分为两部分的过程:

  • 收集所有定义:

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

  • 将任何非 __dunder___sunder_descriptor 的内容转换为枚举成员:

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

这意味着在创建 ALLNAMES 时,AB 仍然是 int,而 ints 没有 to_string 方法。解决这个问题的简单方法是在尝试访问这些方法之前检索枚举成员:self.Inner(e).to_string().

为了将它们组合在一起,您的代码应该如下所示:

# 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()

当 运行 时,这就是你得到的:

[1, 2]
some_name_other_than_enum_a_name
some_name_other_than_enum_b_name