Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to specify a default value for python enums?

Tags:

python

enums

Given the following enum:

class MyEnum(IntEnum):

    A = 0
    B = 1
    C = 2

How can I specify a default value. I want to be able to do:

my_val = MyEnum()

and havemy_val be <MyEnum.A: 0>

Is this possible? I've tried customizing __new__, __init__, __call__ but I can't get it to work.

like image 865
Stephen Avatar asked Jul 02 '17 04:07

Stephen


4 Answers

falsetru's answer would be the way to go if 0 argument case needs to be catered.

If it is only about returning default when given value does not exist, we can override _missing_ hook in Enum class (Since Python 3.6):

from enum import IntEnum


class MyEnum(IntEnum):
    A = 0
    B = 1
    C = 2

    @classmethod
    def _missing_(cls, value):
        return cls.A

assert MyEnum(0) is MyEnum.A
assert MyEnum(1) is MyEnum.B
assert MyEnum(-1) is MyEnum.A
assert MyEnum(None) is MyEnum.A
like image 197
Charles Lin Avatar answered Oct 03 '22 21:10

Charles Lin


MyEnum(..) is handled by EnumMeta.__call__. You need to override that method:

from enum import EnumMeta, IntEnum


class DefaultEnumMeta(EnumMeta):
    default = object()

    def __call__(cls, value=default, *args, **kwargs):
        if value is DefaultEnumMeta.default:
            # Assume the first enum is default
            return next(iter(cls))
        return super().__call__(value, *args, **kwargs)
        # return super(DefaultEnumMeta, cls).__call__(value, *args, **kwargs) # PY2


class MyEnum(IntEnum, metaclass=DefaultEnumMeta):
    # __metaclass__ = DefaultEnumMeta  # PY2 with enum34
    A = 0
    B = 1
    C = 2


assert MyEnum() is MyEnum.A
assert MyEnum(0) is MyEnum.A
assert MyEnum(1) is not MyEnum.A
like image 35
falsetru Avatar answered Oct 03 '22 23:10

falsetru


Update

As of aenum 2.2.11 calling an Enum without a value is supported:

from aenum import Enum, no_arg

class Color(Enum):
    black = 0
    red = 1
    green = 2
    blue = 3
    #
    @classmethod
    def _missing_value_(cls, value):
        if value is no_arg:
            return cls.black

and in use:

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

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

>>> Color(4)
Traceback (most recent call last):
  ...
ValueError: 4 is not a valid Color

Original Answer

If you do not control the Enums, then do:

my_val = list(MyEnum)[0]

or, if the Enum might be empty:

my_val = len(MyEnum) and list(MyEnum)[0] or None

If you do control the Enums, then add a get_default method:

class MyEnum(Enum):
    def get_default(self):
        return self.A

or, even simpler, make default be an alias:

class MyEnum(Enum):
    A = 0
    B = 1
    C = 2
    default = A

You could also wrap the first method in to a function:

def default_member(enum):
    return list(enum)[0]

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

like image 44
Ethan Furman Avatar answered Oct 03 '22 22:10

Ethan Furman


Why don’t you just use the standard syntax?

my_val = MyEnum.A

If you really want to do this, you may have to write your own enum meta override class. You can see this example for the implementation in cpython so that you assign a default value equal to the first value in the value map for the enum.

like image 24
2ps Avatar answered Oct 03 '22 21:10

2ps