1. When and where to use enums?
- When you have a variable that takes one of a limited set of possible values.
For example, the days of the week:
class Weekday(Enum):
MONDAY = 1
TUESDAY = 2
WEDNESDAY = 3
THURSDAY = 4
FRIDAY = 5
SATURDAY = 6
SUNDAY = 7
2. Why do we need enum? What are the advantages?
Enums are advantageous because they give a name to a constant, which makes code more readable; and because the individual members cannot be rebound, making Python Enums semi-constant (because the Enum
itself could still be rebound).
Besides more readable code, debugging is also easier as you see a name along with the value, not just the value
Desired behavior can be added to Enums
For example, as anyone who has worked with the datetime module knows, datetime
and date
have two different representations for the days of the week: 0-6 or 1-7. Rather than keep track of that ourselves we can add a method to the Weekday
enum to extract the day from the datetime
or date
instance and return the matching enum member:
@classmethod
def from_date(cls, date):
return cls(date.isoweekday())
3. What exactly is Enum?
Enum is a type, whose members are named constants, that all belong to (or should) a logical group of values. So far I have created Enum
s for:
- the days of the week
- the months of the year
- US Federal Holidays in a year
FederalHoliday
is my most complex; it uses this recipe, and has methods to return the actual date the holiday takes place on for the year given, the next business day if the day in question is a holiday (or the range of days skipped includes the holiday or weekends), and the complete set of dates for a year. Here it is:
class FederalHoliday(AutoEnum):
NewYear = "First day of the year.", 'absolute', Month.JANUARY, 1
MartinLutherKingJr = "Birth of Civil Rights leader.", 'relative', Month.JANUARY, Weekday.MONDAY, 3
President = "Birth of George Washington", 'relative', Month.FEBRUARY, Weekday.MONDAY, 3
Memorial = "Memory of fallen soldiers", 'relative', Month.MAY, Weekday.MONDAY, 5
Independence = "Declaration of Independence", 'absolute', Month.JULY, 4
Labor = "American Labor Movement", 'relative', Month.SEPTEMBER, Weekday.MONDAY, 1
Columbus = "Americas discovered", 'relative', Month.OCTOBER, Weekday.MONDAY, 2
Veterans = "Recognition of Armed Forces service", 'relative', Month.NOVEMBER, 11, 1
Thanksgiving = "Day of Thanks", 'relative', Month.NOVEMBER, Weekday.THURSDAY, 4
Christmas = "Birth of Jesus Christ", 'absolute', Month.DECEMBER, 25
def __init__(self, doc, type, month, day, occurrence=None):
self.__doc__ = doc
self.type = type
self.month = month
self.day = day
self.occurrence = occurrence
def date(self, year):
"returns the observed date of the holiday for `year`"
if self.type == 'absolute' or isinstance(self.day, int):
holiday = Date(year, self.month, self.day)
if Weekday(holiday.isoweekday()) is Weekday.SUNDAY:
holiday = holiday.replace(delta_day=1)
return holiday
days_in_month = days_per_month(year)
target_end = self.occurrence * 7 + 1
if target_end > days_in_month[self.month]:
target_end = days_in_month[self.month]
target_start = target_end - 7
target_week = list(xrange(start=Date(year, self.month, target_start), step=one_day, count=7))
for holiday in target_week:
if Weekday(holiday.isoweekday()) is self.day:
return holiday
@classmethod
def next_business_day(cls, date, days=1):
"""
Return the next `days` business day from date.
"""
holidays = cls.year(date.year)
years = set([date.year])
while days > 0:
date = date.replace(delta_day=1)
if date.year not in years:
holidays.extend(cls.year(date.year))
years.add(date.year)
if Weekday(date.isoweekday()) in (Weekday.SATURDAY, Weekday.SUNDAY) or date in holidays:
continue
days -= 1
return date
@classmethod
def year(cls, year):
"""
Return a list of the actual FederalHoliday dates for `year`.
"""
holidays = []
for fh in cls:
holidays.append(fh.date(year))
return holidays
Notes: