6
\$\begingroup\$

My aim is to assign a color to each workday in a week. So if a user enters a day as a str, then I can know which color it should have.

Because colors and workdays are fixed, I make them as enum.Enum instance.

  1. Is enum.Enum a proper choice for colors and workdays?
  2. To link day and color, I choose dictionary for weekday_color. Is there a better choice?
from enum import Enum


class WeekDay(Enum):
    MONDAY = 'Monday'
    TUESDAY = 'Tuesday'
    WEDNESDAY = 'Wednesday'
    THURSDAY = 'Thursday'
    FRIDAY = 'Friday'


class Color(Enum):
    RED = 'Red'
    GREEN = 'Green'
    BLUE = 'Blue'


weekday_color = {
    WeekDay.MONDAY: Color.RED,
    WeekDay.TUESDAY: Color.GREEN,
    WeekDay.WEDNESDAY: Color.BLUE,
    WeekDay.THURSDAY: Color.GREEN,
    WeekDay.FRIDAY: Color.RED,
}


def main():
    today_input = 'Monday'
    today = WeekDay(today_input)
    today_color = weekday_color[today]
    print(today)
    print(today_color)


if __name__ == '__main__':
    main()
\$\endgroup\$
1
  • \$\begingroup\$ weekday_color seems to indicate a single element with a single color. weekday_colorscheme or colorscheme_for_weekdays maybe? \$\endgroup\$ Jan 29, 2020 at 5:19

2 Answers 2

5
\$\begingroup\$

Design-wise, using an Enum is entirely appropriate -- they were added to make "magic" constants easier to work with by giving them informative debugging names and easier containment checks.

Using a dictionary to tie the two enums together is unnecessary in this case as it makes more sense, and is easily possible, to have color be an attribute of each Weekday.

Whether Color is itself an enum depends on what you will be doing with the colors:

  • do you need actual color values?
  • is the color value going to be sent to another system, or stay inside your own program?

Whether or not you have a separate Color enum, a good way to combine those two things is like so (shown using aenum1, but can be done with the stdlib Enum2:

from aenum import AutoNumberEnum

class WeekDay(AutoNumberEnum):
    _init_ = 'color'
    #
    MONDAY = 'red'
    TUESDAY = 'green'
    WEDNESDAY = 'blue'
    THURSDAY = 'green'
    FRIDAY = 'red'

and in use:

>>> list(WeekDay)
[<WeekDay.MONDAY: 1>, <WeekDay.TUESDAY: 2>, <WeekDay.WEDNESDAY: 3>, <WeekDay.THURSDAY: 4>, <WeekDay.FRIDAY: 5>]

>>> WeekDay.MONDAY
<WeekDay.MONDAY: 5>

>>> WeekDay['MONDAY']
<WeekDay.MONDAY: 5>

>>> WeekDay.MONDAY.color
'red'

If you decide to use an enum for color, simply assign that instead of, for example, 'red'.


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


2 The stdlib version (without the members):

class WeekDay(Enum):
    #
    def __new__(cls, color):
        value = len(cls.__members__) + 1
        obj = object.__new__(cls)
        obj._value_ = value
        obj.color = color
        return obj
    #
    # members here
\$\endgroup\$
1
  • \$\begingroup\$ This goes against my sense of correct class design. Weekdays are only assigned colors when they are considered in e.g. a calendar. But even then it is only the decoration of the calendar. Colors are not properties of weekdays. This will quickly become apparent if the weekday also e.g. needs to be displayed in gray tones. Now we have a weekday with 2 colors? \$\endgroup\$ Jan 29, 2020 at 5:16
4
\$\begingroup\$

Given your example, I don't see much advantage of using enums. I'd keep it simple and do

weekday_color = {"Monday": "RED", "Tuesday": "GREEN", ...}

Now, if you want to keep going down the enum route, you can make them more useful. For example the Color enum, instead of having the strings as values, you can pack information like the code the color has

class Color(Enum):
    RED = 'FF0000'
    GREEN = '00FF00'
    BLUE = '0000FF'

Then you reduce duplication and pack more information that could be useful later on.

Concerning the Weekday enum, I'd change it as follows

from enum import IntEnum, auto

class Weekday(IntEnum):
    MONDAY = auto()
    TUESDAY = auto()
    WEDNESDAY = auto()
    THURSDAY = auto()
    FRIDAY = auto()

With this, you get comparison/sorting of the weekdays in a way that feels natural. And by using auto() you don't retype the name of the weekday as value. Although you lose the possibility of doing something like Weekday("Monday"), but then you access it like `Weekday["MONDAY"] to access by name.

Thus

from enum import Enum, IntEnum, auto


class Weekday(IntEnum):
    MONDAY = auto()
    TUESDAY = auto()
    WEDNESDAY = auto()
    THURSDAY = auto()
    FRIDAY = auto()


class Color(Enum):
    RED = 'FF0000'
    GREEN = '00FF00'
    BLUE = '0000FF'


weekday_color = {
    Weekday.MONDAY: Color.RED,
    Weekday.TUESDAY: Color.GREEN,
    Weekday.WEDNESDAY: Color.BLUE,
    Weekday.THURSDAY: Color.GREEN,
    Weekday.FRIDAY: Color.RED,
}


def main():
    today_input = 'Monday'
    today = Weekday[today_input.upper()]  # adding uppercasing to be resilient
    today_color = weekday_color[today]
    print(f"{today.name}'s color is {today_color.name} with code {today_color.value}") 


if __name__ == '__main__':
    main()  # output: MONDAY's color is RED with code FF0000
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.