How do I create enums in Python 3.11+ with no arguments passed to __init__?

152 views Asked by At

I have the following code, which works as expected on 3.9 and 3.10:

from enum import Enum


class Environment(Enum):
    DEV = ()
    INT = ()
    PROD = ()

    def __init__(self):
        self._value_ = self.name.lower()

    def __str__(self):
        return self.name

    def get_url(self):
        if self is Environment.DEV:
            return 'http://localhost'
        else:
            return f'https://{self.value}.my.domain'

Here is the result in a REPL:

>>> Environment.DEV.get_url()
'http://localhost'
>>> Environment.INT.get_url()
'https://int.my.domain'
>>> Environment.PROD.get_url()
'https://prod.my.domain'

I did this because I read that defining an __init__ method for enums allows passing tuples when assigning values, and I wanted to avoid the redundant/error prone explicit declarations such as:

    DEV = 'DEV'
    INT = 'INT'
    PROD = 'PROD'

Now, when I run the same code on Python 3.11 or 3.12, every call to get_url returns the localhost URL. Trying it out in the REPL, we get:

>>> Environment.DEV.get_url()
'http://localhost'
>>> Environment.INT.get_url()
'http://localhost'
>>> Environment.PROD.get_url()
'http://localhost'

I even noticed that all of the enum values are equal to the first one:

>>> Environment.DEV
<Environment.DEV: 'dev'>
>>> Environment.INT
<Environment.DEV: 'dev'>
>>> Environment.PROD
<Environment.DEV: 'dev'>

Has something changed in python 3.11 which makes using empty tuples at assignation forbidden/broken? How could I get the same result in those versions, i.e. declare the enum values without having to explicitly pass any argument to __init__?

1

There are 1 answers

0
user2357112 On BEST ANSWER

You could use auto() and _generate_next_value_:

from enum import Enum, auto

class Environment(Enum):
    def _generate_next_value_(name, start, count, last_values):
        return name
    DEV = auto()
    INT = auto()
    PROD = auto()

This will automatically generate values based on the names.

Note that while _generate_next_value_ is currently documented as a staticmethod, applying the staticmethod decorator is optional, and didn't work on previous versions - previous versions required a bare function.