1

I want to write class for Enums in Python.

My issue is that I want to have same value for different enum inputs. For example:

class Animal(Enum):
    Cat = ['Perian cat', 'Bengal cat', 'Siamese cat']

So then I could use it like this:

some_animal = Animal('Persian cat')
print(some_animal)
>> Animal.Cat

I think it is not possible, but just to be sure I wanted to ask for that here.

UPDATE

I tried this solution:

class _Cat(Enum):
 BENGAL = 'Bengal cat'
 PERSIAN = 'Persian cat'
 SIAMESE = 'Siamese cat'

class Animal(Enum):
 Cat = _Cat

It works in that sense that I can access the values of the Cat class, but what I would like to achieve is something like this:

some_animal = Animal('Persian cat')
print(some_animal)
>> Animal.Cat.PERSIAN

Thanks.

JustPawel
  • 165
  • 1
  • 9
  • 1
    What you would want is probably a class that stores the given name and associated enum value (both as fields). Not just enum. Enum is basically a bunch of singletons - even if you were to assign some attribute - e.g. `Animal.Cat.something = 'test'`, then `Animal('Bengal cat').something` will also be set. Because all Animal.Cat is the same object (`is`, not only `==`) – h4z3 Dec 17 '21 at 11:18
  • Thank you for your comment. Could you write short example, please? I think I don't get it all the way. – JustPawel Dec 17 '21 at 11:26
  • What I meant: make a class that holds 2 things: your full description and your enum – h4z3 Dec 17 '21 at 11:30

2 Answers2

2

You can obtain a close result with the functional API. In fact the hard part in not to have multiple members with same value, but to have names which contain spaces (ie: cannot be identifiers):

Animal = Enum('Animal', (('Persian cat', 'cat'), ('Bengal cat', 'cat'),
                         ('Siamese cat', 'cat')))

Then you can do:

>>> print(Animal['Persian cat'].value)
cat

And you can control the equality of the members:

>>>Animal['Persian cat'] == Animal['Bengal cat']
True

But then your enum class becomes very close to a plain dict. If additionaly you intend to be able to add new members, then IMHO, it is a hint that what you want is not an Enum but a simple dict.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • Thank for your reply. What about case in which I don't have spaces i.e. `persian_cat, bengal_cat` etc. Is it possible to write a class so I could edit class methods on the way as well? I think it is not possible with functional API. Simple scenario: I have class the way I described it but additionally if I put there an Enum which is not described in the class I can do something with it. – JustPawel Dec 17 '21 at 11:38
  • @JustPawel: are you sure that a `dict` would not better meet your needs? (just see my edit...) – Serge Ballesta Dec 17 '21 at 12:47
1

The easiest method would be to use the MultiEnum from aenum:

from aenum import MultiValueEnum

class Animal(MultiValueEnum):
    CAT = 'Cat', 'Persian cat', 'Bengal cat', 'Siamese cat'
    DOG = 'Dog', 'Greyhound', 'Boxer', 'Great Dane'
    def __repr__(self):
        # make the repr not reduntant
        return "<%s.%s>" % (self.__class__.__name__, self.name)

and in use:

>>> Animal('Bengal cat')
<Animal.CAT>

>>>> Animal('Boxer')
<Animal.DOG>

If you need to stick with the stdlib version of Enum:

from enum import Enum

class Animal(Enum):
    #
    def __new__(cls, *values):
        member = object.__new__(cls)
        member._value_ = values[0]
        member.all_values = values
        return member
    #
    @classmethod
    def _missing_(cls, value):
        for member in cls:
            if value in member.all_values:
                return member
    #
    CAT = 'Cat', 'Persian cat', 'Bengal cat', 'Siamese cat'
    DOG = 'Dog', 'Greyhound', 'Boxer', 'Great Dane'
    def __repr__(self):
        # make the repr not reduntant
        return "<%s.%s>" % (self.__class__.__name__, self.name)

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