如何腌制 Python 中的复杂枚举值

How to pickle complex enum values in Python

当我尝试解开 pickled 复杂枚举实例时,我总是得到 "ValueError: BLUE is not a valid Colors"。

有什么方法可以pickle和unpickle吗?

from pickle import loads, dumps
from enum import Enum


class ColorType(object):

    def __init__(self, counter, name):
        self.counter = counter
        self.name = name

    def __str__(self):
        return self.name


class Colors(Enum):
    GREEN = ColorType(1, 'GREEN')
    BLUE = ColorType(2, 'BLUE')


color = Colors.BLUE
print(color is loads(dumps(color)))

我正在使用 Python 2.7.

不要使用自定义 class 作为枚举值;这里没有必要。您的具体示例根本不需要单独的 class,您可以使用:

class Colors(Enum):
    GREEN = 1
    BLUE = 2

    def __str__(self):
        return self.name

    @property
    def counter(self):
        return self.value

这具有 更好的 str().counter 行为;您的代码要求 str() 应用于 Color.<name>.value 而不是直接应用于 Color.<name>.

对于其他自定义方法和属性,将它们直接放在 Enum subclass 上,它们也将成为枚举成员的一部分。如果每个条目需要更多值,请设置 tuple 并在 __init__ 方法中将该元组分开。该文档有一个很好的 Planet example 进一步说明了这一点。

演示:

>>> Colors.BLUE
<Colors.BLUE: 2>
>>> Colors.BLUE.value
2
>>> Colors.BLUE.counter
2
>>> str(Colors.BLUE)
'BLUE'
>>> Colors.BLUE is loads(dumps(Colors.BLUE))
True

这里的问题是基本平等:

>>> ColorType(2, 'BLUE') == ColorType(2, 'BLUE')
False

因此,当 Colors 试图为 ColorType(2, 'BLUE') 的 unpickled 值找到一个匹配项时,它失败了。

解决方案很简单:将 __eq____ne__ 方法添加到 `ColorType':

class ColorType(object):

    def __init__(self, counter, name):
        self.counter = counter
        self.name = name

    def __str__(self):
        return self.name

    def __eq__(self, other):
        return self.name == other.name and self.counter == other.counter

    def __ne__(self, other):
        # not needed in Python 3
        return self.name != other .name or self.counter != other.counter

NB 我同意@MartijnPieters 的观​​点,在大多数情况下,您应该只将所需的功能添加到 Enum 本身。