在Python枚举

时间:2018-05-18 05:56:25

标签: python enums spyder

我正在使用基于图形的框架,该框架由几种类型的节点和关系组成。这些类型中的每一种都有一些我希望能够轻松访问的元属性。为了表示这些类型,我认为枚举将是最佳选择。

我对Python仍然比较陌生,但我知道来自其他语言的枚举,比如Java。我想这个问题真的归结为这是否很好地利用了Python中的枚举。我代表我的类型:

class Grammar(Enum):
    VERB = 'verb'
    NOUN = 'noun'
# END Grammar

class _Word():
    def __init__(self, name, meta):
        self.name = name
        self.meta = meta
    # END __init__
# END _Word

class Word(Enum):
    TYPE_A = _Word('foo', Grammar.VERB)
    TYPE_B = _Word('bar', Grammar.NOUN)
# END Word

因此,我的每个Word值都分配了一个_Word对象,这是一个复杂的类型。在使用Enum的大多数情况下,这种方法很好。但是,我的同事注意到Spyder在检查存在枚举实例的对象时抛出异常:ValueError(): is not a valid Word(注意冒号后面的空格)。

这让我觉得我使用枚举的方式不是最好的做法。我错过了什么吗?

1 个答案:

答案 0 :(得分:0)

问题似乎是像pandas和json这样的几个库很难序列化enum对象。我在网上找到了几个解释如何编写自定义序列化器的解决方案。但是,由于我不能告诉Spyder或Pandas使用该序列化器,我需要不同的东西。

在玩了一下之后,我发现了两种解决我问题的方法。不幸的是,使用Enums的解决方案不适用于Python 2.7,这是我的项目的要求。因此,我将解释两者。

Python 3.4:

我将我的值的数据类型添加为我的Enum类的mixin,并按建议here注释我的枚举值的赋值。特别是对于数据帧,我还需要使我的枚举对象具有可比性,我通过定义比较方法来做到这一点。最后将Enum值传递给我的复杂对象的构造函数。这消除了通过value属性访问属性的需要。

class Grammar(str, Enum):
    VERB: str = 'verb'
    NOUN: str = 'noun'

    def __eq__(self, other):
        return not self < other and not other < self
    # END __eq__

    def __ne__(self, other):
        return self < other or other < self
    # END __ne__

    def __gt__(self, other):
        return other < self
    # END __gt__

    def __ge__(self, other):
        return not self < other
    # END __ge__

    def __le__(self, other):
        return not other < self
    # END __le__

    def __lt__(self, other):
        return self.value < other.value
    # END __lt__

    def __hash__(self):
        return hash(self.value)
    # END __hash__
# END Grammar

class _Word(dict):
    def __init__(self, name, meta):
        self['name'] = name
        self['meta'] = meta
    # END __init__
# END _Word

class Word(dict, Enum):
    TYPE_A: dict = _Word('foo', Grammar.VERB)
    TYPE_B: dict = _Word('bar', Grammar.NOUN)

    def __init__(self, _word):
        self['name'] = _word['name']
        self['meta'] = _['meta']
    # END __init__

    def __eq__(self, other):
        return not self < other and not other < self
    # END __eq__

    def __ne__(self, other):
        return self < other or other < self
    # END __ne__

    def __gt__(self, other):
        return other < self
    # END __gt__

    def __ge__(self, other):
        return not self < other
    # END __ge__

    def __le__(self, other):
        return not other < self
    # END __le__

    def __lt__(self, other):
        return self['name'] < other['name']
    # END __lt__

    def __hash__(self):
        return hash(self['name'])
    # END __hash__
# END Word

这让Spyder可以毫无问题地处理我的枚举。作为奖励,当使用带有数据帧的对象或序列化为json时,通用对象表示现在被替换为实际值。这让生活变得轻松!

Python 2.7: 不幸的是,Python 2.7的Enum实现并不包含使我的第一个解决方案工作所需的所有语法。具体而言,不允许VERB: str = 'verb'带注释的分配。我最终删除了使用Enums并转而使用类属性。这仍然允许以明确表示您正在处理常量(例如Grammar.VERB)的方式进行访问,但这意味着您将丢失可用的Enum功能。

对我来说最重要的是能够遍历我的枚举值,所以我创建了一个函数,允许我从伪枚举中检索所有值:

class PseudoEnum():

    @classmethod
    def getAll(cls):

        """
        Returns a list of tuples representing all entries for this PseudoEnum along the dimensions key x value. This is useful when you need to iterate over the enum.

        :returns: A list of all PseudoEnum entries
        :rtype: list of tuple
        """

        return [(i, getattr(cls, i)) for i in dir(cls) if not i.startswith('__') and not callable((getattr(cls, i)))]
    # END getAll        
# END PseudoEnum

GrammarWord现在继承自PseudoEnum。此外,比较方法已从Word移至_WordGrammar不再需要比较方法,因为它处理常规字符串。

为漫长的回答道歉。希望这有帮助!