4

I want to create an enum that stores a binary value, but to which I can pass any value (it will store value % 2).

That is : I want to use the "official" Enum type from Python3 but change the constructor.

I tried this code : 

from enum import Enum

class Color(Enum):
    black = 0
    red = 1

    def __new__(cls, value):
        super().__new__(cls, value % 2)

    def __str__(self):
        return self.name

But I get an error :

>>> from ZCasino import *
ValueError: 0 is not a valid Couleur

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/felix/Téléchargements/ZCasino.py", line 12, in <module>
    class Couleur(Enum):
  File "/usr/lib/python3.8/enum.py", line 212, in __new__
    enum_member = __new__(enum_class, *args)
  File "/home/felix/Téléchargements/ZCasino.py", line 18, in __new__
    super().__new__(cls, value)
  File "/usr/lib/python3.8/enum.py", line 595, in __new__
    raise exc
  File "/usr/lib/python3.8/enum.py", line 579, in __new__
    result = cls._missing_(value)
  File "/usr/lib/python3.8/enum.py", line 608, in _missing_
    raise ValueError("%r is not a valid %s" % (value, cls.__name__))
ValueError: 0 is not a valid Couleur
Ethan Furman
  • 63,992
  • 20
  • 159
  • 237
Salamandar
  • 589
  • 6
  • 16
  • 1
    Does this answer your question? [Is it possible to override \_\_new\_\_ in an enum to parse strings to an instance?](https://stackoverflow.com/questions/24105268/is-it-possible-to-override-new-in-an-enum-to-parse-strings-to-an-instance) – AMC Feb 11 '20 at 16:25
  • 1
    Yeah, but I did "answer your own question", I thought the question would be flagged as such to people answering ^^'' – Salamandar Feb 12 '20 at 08:10

3 Answers3

12

You can take advantage of the new _missing_ method:

from aenum import Enum

class Color(Enum):
    black = 0
    red = 1
    #
    def __str__(self):
        return self.name
    #
    @classmethod
    def _missing_(cls, value):
        return cls(value % 2)

and in use:

>>> list(Color)
[<Color.black: 0>, <Color.red: 1>]

>>> Color(3)
<Color.red: 1>

>>> Color(8)
<Color.black: 0>

_missing_ was introduced in Python 3.6. If you need to support earlier Pythons you can use the aenum1 library.

NB When calling Color(7) you are not creating a new Color, but getting the existing <Color.black: 0> that was created when the Enum itself was created.


1 Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.

Ethan Furman
  • 63,992
  • 20
  • 159
  • 237
3

A new class method would probably be a better idea than overriding __new__:

class Color(Enum):
    black = 0
    red = 1

    @classmethod
    def from_int(cls, x):
        return cls(x%2)

Then

>>> Color.from_int(6)
<Color.black: 0>
>>> Color.from_int(7)
<Color.red: 1>
chepner
  • 497,756
  • 71
  • 530
  • 681
2

You need to override the __new__ method after the method definition :

(Is it possible to override __new__ in an enum to parse strings to an instance?)

from enum import Enum

class Color(Enum):
    black = 0
    red = 1

    def __str__(self):
        return self.name

Couleur.__new__ = lambda cls, value: super(Couleur, cls).__new__(cls, value % 2)

Salamandar
  • 589
  • 6
  • 16