Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Support unknown values in Python Enums

I would like to extend Python Enums to support non-defined values.

My use case: I want to benefit from Enums (e.g., being able to address/compare known entries with speaking names) but I also want it to support unknown values. If there's no name behind the value, str should simply print the str representation of the value and comparisons should fail.

Let me give you a short example what I'd like to have:

from enum import Enum

class Foo(Enum):
    A = 1
    B = 2
    C = 3

    def __str__(self):
        return self.name


print(Foo(1)) # prints 'A'
print(Foo(2)) # print 'B'
print(Foo(3)) # prints 'C'
print(Foo(1) == Foo.A) # prints 'true'

print(Foo(4)) # I'd expect '4'
print(Foo(123)) # I'd expect '123'
print(Foo(123) == Foo.A) # I'd expect False

Of course the last lines fail.

Is there a way to extend Enums, or is there maybe another easy pythonic way? (No third party libraries, please.)

like image 596
rralf Avatar asked May 05 '19 21:05

rralf


People also ask

Can enum have multiple values Python?

Enums can't have multiple value per name.

Can you assign values to enums?

Enum ValuesYou can assign different values to enum member. A change in the default value of an enum member will automatically assign incremental values to the other members sequentially.

Can enums hold values?

Overview. The Java enum type provides a language-supported way to create and use constant values. By defining a finite set of values, the enum is more type safe than constant literal variables like String or int.

Can enums have the same value Python?

By definition, the enumeration member values are unique. However, you can create different member names with the same values. In this example, the Color enumeration has the RED , CRIMSON , and SALMON members with the same value 1.

How to use enums in Python?

Enums can be displayed as string or repr. 2. Enums can be checked for their types using type (). 3. “ name ” keyword is used to display the name of the enum member. 4. Enumerations are iterable. They can be iterated using loops

What are enumerations in Python?

Enumerations are Python classes, and can have methods and special methods as usual. If we have this enumeration: >>> class Mood(Enum): ... FUNKY = 1 ...

What are the advantages of enums in Java?

Enums can be displayed as string or repr. 2. Enums can be checked for their types using type (). 3. “ name ” keyword is used to display the name of the enum member. 4. Enumerations are iterable. They can be iterated using loops 5. Enumerations support hashing. Enums can be used in dictionaries or sets.

How do I make an enum member’s value depend on its value?

To make your own Enum’s boolean evaluation depend on the member’s value add the following to your class: Enum classes always evaluate as True. If you give your Enum subclass extra methods, like the Planet class above, those methods will show up in a dir () of the member, but not of the class:


2 Answers

To make this work across multiple values of Python, particularly Python 2, you are going to need an external library: aenum1.

from aenum import Enum, extend_enum

class Foo(Enum):
    _order_ = 'A B C'   # because Python 2 does not keep order
    A = 1
    B = 2
    C = 3

    def __str__(self):
        return self.name

    @classmethod
    def _missing_(cls, value):
        extend_enum(cls, str(value), (value, ))
        return list(cls)[-1]

This dynamically adds the unknown value as an enum member and returns it.


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

like image 119
Ethan Furman Avatar answered Sep 18 '22 14:09

Ethan Furman


Here's another answer that works in Python 3.6, 3.7 and 3.8.

It involves weird metaclass hackery, so be wary of that.

import enum

class TestMeta(enum.EnumMeta):
    def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1):
        if names is not None:
            # the enum is being constructed via the functional syntax
            return super().__call__(value, names=names, module=module, qualname=qualname, type=type, start=start)

        try:
            # attempt to get an enum member
            return super().__call__(value, names=names, module=module, qualname=qualname, type=type, start=start)
        except ValueError:
            # no such member exists, but we don't care
            return value

class Test(enum.Enum, metaclass=TestMeta):
    A = 5
    B = 6    

print(Test(5), Test(6), Test(7))

This version works in Python 2.7, but requires a third-party library for enum (see comments):

class TestMeta(enum.EnumMeta):
    def __call__(cls, value, names=None, module=None, type=None, start=1):
        if names is not None:
            return enum.EnumMeta.__call__(cls, value, names, module, type, start)

        try:    
            return enum.EnumMeta.__call__(cls, value, names, module, type, start)
        except ValueError:
            return value
like image 35
ForceBru Avatar answered Sep 17 '22 14:09

ForceBru