When I try to unpickle pickled complex enum instance, I always get "ValueError: BLUE is not a valid Colors".
Is there any way how to pickle and 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)))
I am using Python 2.7.
Don't use a custom class as the enum values; there is no need here. Your specific example doesn't need a separate class at all, you could just use:
class Colors(Enum):
    GREEN = 1
    BLUE = 2
    def __str__(self):
        return self.name
    @property
    def counter(self):
        return self.value
This has better str() and .counter behaviour; your code requires str() to be applied to Color.<name>.value rather than directly to Color.<name>.
For other custom methods and attributes, put those directly on the Enum subclass and they'll be part of the enum members too. If you need more values per entry, set a tuple and pull that tuple apart in a __init__ method. The documentation has an excellent Planet example that illustrates this further.
Demo:
>>> 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
                        The issue here is basic equality:
>>> ColorType(2, 'BLUE') == ColorType(2, 'BLUE')
False
So when Colors is trying to find a match for the unpickled value of ColorType(2, 'BLUE') it is failing.
The solution is simple:  add the __eq__ and __ne__ methods to `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 I agree with @MartijnPieters that in most cases you should just add the needed functionality to the Enum itself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With