Enums в Python

Опубликовано 17 February 2017 в Python

Enums наименее используемая фича Питона. Как программисты мы предпочитаем использовать странные дикты или списки там, где мы могли бы использовать enum. По большей части это происходит из-за того, что это довольно новая фича и требуется использовать внешнюю библиотеку обратной совместимости, если используется питон версии 2.7. Тем не менее, довольно много случаев, когда использовать enum гораздо удобнее.

В любом проекте на Python легко найти код, который делает проверки против строковых литералов. Подобные конструкции используются для определения типов постов, ролей клиентов и тому подобное. Конечно этот код меняется со временем. Меняется набор используемых строковых литералов. Это делает такой код трудно поддерживаемым и трудно тестируемым.

К примеру, у нас есть такой код.

if a.type == 'article':
    do_something_if_article(a)
elif a.type == 'review':
    do_something_if_review(a)
else:
    do_something()

С ним все в порядке? Не совсем. Что если мы решим поменять имя типа с "review" на "reviews"? В этом случае вместо вызова do_something_if_review мы вызовем do_something. К тому же этот код не так-то легко модифицировать. Имя типа может быть использовано в множестве мест в разных контекстах. Можно ли код улучшить? Легко!

ARTICLE_TYPE = 'article'
REVIEW_TYPE = 'review'

if a.type == ARTICLE_TYPE:
    do_something_if_article(a)
elif a.type == REVIEW_TYPE:
    do_something_if_review(a)
else:
    do_something()

Теперь мы используем константы вместо строковых литералов. Так на много лучше. Мы сократили места, где в случае смены имени типа потребуется редактирование, до одного. Но это никак не решило проблему добавления нового типа: нам потребуется добавить еще одну ветку elif. Надо что-то еще улучшить.

Подобный код можно существенно улучшить с помощью enum:

class PostType(Enum):
    ARTICLE = do_something_if_article
    REVIEW = do_something_if_review
    OTHER = do_something

    @classmethod
    def of(cls, type_name):
        return getattr(cls, type_name.upper(), cls.OTHER)

    def do_stuff(self):
        return self.value()

PostType.of(a.type).do_stuff()

Это уже выглядит прилично. К тому же такой код гораздо гибче предыдущих вариантов. Я уверен, в ваших проектах есть множество мест, где подобный подход сильно улучшит качество кода. И у вас нет оправданий не пойти и не поправить эти места!

P.S. pip install enum34 для Python 2.7

---
Возник вопрос? Мне всегда можно написать в Twitter: avkorablev