Stephen
Stephen

Reputation: 2823

Is there a way to specify a default value for 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.

Upvotes: 25

Views: 38170

Answers (5)

Charles Lin
Charles Lin

Reputation: 712

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

Upvotes: 51

koty0f
koty0f

Reputation: 11

I also stumbled upon this problem. Posting my solution for Python 3.9.5

from enum import unique, Enum, EnumMeta

class PrinterModelMeta(EnumMeta):
    def __call__(cls, value=-1, *args, **kwargs):
        if value == -1:
            value = PrinterModel.detect_model()
        return super().__call__(value, *args, **kwargs)


@unique
class PrinterModel(Enum, metaclass=PrinterModelMeta):
    # FORBIDDEN = -1
    MODEL0 = 0
    MODEL1 = 1
    MODEL2 = 2

    @classmethod
    def detect_model(cls) -> int:
        model = None
        panel_name = cls.panel_name()
        if panel_name == "model-aaa":
            model = PrinterModel.MODEL0
        if panel_name == "model-bbb":
            model = PrinterModel.MODEL1
        if panel_name == "model-ccc":
            model = PrinterModel.MODEL2

        logger = logging.getLogger(__name__)
        if model is None:
            raise UnknownPrinterModel()
        else:
            return model.value

    @classmethod
    def panel_name(cls) -> str:
        path = Path("/sys/bus/i2c/devices/1-000f/of_node") / "panel-name"
        return path.read_text()[:-1] if path.exists() else None

Upvotes: 1

Ethan Furman
Ethan Furman

Reputation: 69021

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.

Upvotes: 8

falsetru
falsetru

Reputation: 368894

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

Upvotes: 15

2ps
2ps

Reputation: 15916

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.

Upvotes: 1

Related Questions