Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make nested enum also have value

Consider the following code example:

from enum import Enum

class Location(Enum):
    Outside = 'outside'
    Inside = 'inside' 
    class Inside(Enum): # TypeError for conflicting names
        Downstairs = 'downstairs'
        Upstairs = 'upstairs'

How do I make Inside have the value 'inside' whilst also being a nested enum for accessing Downstairs and Upstairs?

Desired input:

print(Location.Inside)
print(Location.Inside.value)
print(Location.Inside.Downstairs)
print(Location.Inside.Downstairs.value)

Desired output:

Location.Inside
inside
Location.Inside.Downstairs
downstairs

UPDATE 1:

Some more context to my specific problem:

class Location(Enum):
    Outside = 'outside'
    Inside = 'inside' 
    class Inside(Enum): # TypeError for conflicting names
        Downstairs = 'downstairs'
        Upstairs = 'upstairs'

class Human:
    def __init__(self, location):
        self.location = location

def getLocationFromAPI():
    # this function returns either 'inside' or 'outside'
    # make calls to external API  
    return location # return location from api in str

def whereInside(human):
    if human.location != Location.Inside:
        return None
    # here goes logic that determines if human is downstairs or upstairs
    return locationInside # return either Location.Downstairs or Location.Upstairs


location_str = getLocationFromAPI() # will return 'inside' or 'outside'
location = Location(location_str) # make Enum
human = Human(location) # create human with basic location
if human.location == Location.Inside:
    where_inside = whereInside(human)
    human.location = where_inside # update location to be more precise

The problem is when I create the Human object I only know of a basic location, as in 'inside' or 'outside'. Only after that can I update the location to be more precise.

like image 775
Dr. Alban Avatar asked Feb 01 '19 23:02

Dr. Alban


2 Answers

You can accomplish this by embedding an enum.Enum inside another like so: (just watch out for names conflicting)

from enum import Enum

class _Inside(Enum):
    Downstairs = 'downstairs'
    Upstairs = 'upstairs'

class Location(Enum):
    Outside = 'outside'
    Inside = _Inside 

print(Location.Inside.value.Downstairs.value)
downstairs
like image 184
Jab Avatar answered Sep 27 '22 20:09

Jab


it may be a bit late and the one who asked the question is no longer necessary, but I leave it here in case someone wants to take a look at it, and even if it has already been validated as one, although the same comment that it is not completely complete .

But I have been thinking about it and in the end I have solved it by looking at the same documentation XD.

You cannot extend classes of Enums, but you can extend methods, I have followed this way and the only thing I have done has been to override the new and init methods, the use case can be modified, this is only to nest enumerators.

from enum import Enum

class SuperNestedEnum(Enum):
    def __new__(cls, *args):
        obj = object.__new__(cls)
        value = None
        # Normal Enumerator definition
        if len(args) == 1:
            value = args[0]

        # Have a tuple of values, first de value and next the nested enum (I will set in __init__ method)
        if len(args) == 2:
            value = args[0]

        if value:
            obj._value_ = value

        return obj

    def __init__(self, name, nested=None):
        # At this point you can set any attribute what you want
        if nested:
            # Check if is an Enumerator you can comment this if. if you want another object
            if isinstance(nested, EnumMeta):
                for enm in nested:
                    self.__setattr__(enm.name, enm)


class Homework(Enum):
    Task = "5"

class Subjects(SuperNestedEnum):
    Maths = "maths"
    English = "english"
    Physics = "nested", Homework

class School(SuperNestedEnum):
    Name = "2"
    Subjects = "subjects", Subjects

Ignore the use case because it doesn't make sense, it's just an example

>>> School.Name
<School.Name: '2'>

>>> School.Subjects
<School.Subjects: 'subjects'>

>>> School.Subjects.value
'subjects'

>>> School.Subjects.Maths
<Subjects.Maths: 'maths'>

>>> School.Subjects.Physics.value
'nested'

>>> School.Subjects.Physics.Task
<Homework.Task: '5'>

>>> School.Subjects.Physics.Task.value
'5'
like image 35
Víctor Quilón Avatar answered Sep 27 '22 22:09

Víctor Quilón