Как сделать перечисление в python
Перечисления в Python
Часто, при создании некой программы в Python требуется указать и некий список константных значений, для дальнейшего использования, будь-то: календарь, список планет, имен и так далее. Для подобных случаев в языке предусмотрен модуль Enum/перечисление.
В данной статье мы рассмотрим функцию перечисления Enum в Python, мощный инструмент для обработки наборов данных, которые не меняются.
Что же такое перечисление в Python?
Enum был добавлен в Python 3.4 в рамках PEP 435. Тем не менее, он доступен вплоть до версии 2.4 через компилятор pypy(версия компилятора Python).
Пример
Мы определяем простой класс (производный от перечисления), содержащий месяцы года. Каждому месяцу присваивается уникальная числовая константа.
from enum import Enum
class Months(Enum) :
JANUARY=1
FEBRUARY=2
MARCH = 3
APRIL=4
MAY=5
JUNE=6
JULY=7
AUGUST=8
SEPTEMBER=9
OCTOBER=10
NOVEMBER=11
DECEMBER=12
Как видно наш класс наследуется от Enum, который расширяет его функциональность.
Доступ к элементам
Есть несколько способов, которыми мы можем это сделать.
# по числовому значению
>>>print (Months(7))
Months.JULY
# используя элемент как индекс
>>>print (Months[‘JULY’])
Months.JULY
# по имени константы
>>>print (Months.JULY)
Months.JULY
# по имени
>>>print (Months.JULY.name)
JULY
# по значению
>>>print (Months.JULY.value)
JULY
И наконец, перебор элементов Python перечисления производится циклом for
for month in (Months):
print (month.name)
Таким образом, перечисления в Python упрощают создание и использование пользовательских спиcков.
Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!
Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/rusakovmy.
Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления
Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.
Порекомендуйте эту статью друзьям:
Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):
Комментарии ( 0 ):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.
Copyright © 2010-2021 Русаков Михаил Юрьевич. Все права защищены.
Enum в Python
В большинстве современных языках программирования поддерживается работа с перечислениями и Python – не исключение. Программист может обойтись без них. Однако, как и многие другие инструменты, перечисления в Python упрощают написание и редактирования кода.
Что такое enum в Python
Enum — это один из модулей языка Python, с английского дословно переводится, как перечисление, что полностью отражает суть модуля.
Перечисления — это набор ограниченных неизменяемых значений, которые можно присвоить переменной. Например:
Подключение
Модуль enum является частью стандартной библиотеки Python 3, поэтому его не нужно отдельно устанавливать, достаточно подключить нужный класс с помощью команды import:
Если по какой-то причине интерпретатор Python не может найти модуль enum, установите его с помощью команды:
Создание
Перечисления создаются с использованием синтаксиса классов. Это упрощает их чтение и написание. Пример:
В результате мы получим следующее:
Значение элементов могут быть представлены любыми типами (str, int и другие). Структура перечисления следующая:
Программист может не прописывать значения элементов, вместо этого он может использовать функцию auto(), которая автоматически присваивает элементу значение «1», а каждому последующему — значение на единицу больше:
Свойства
Поддержка итераций
Перечисления — это итерируемые объекты, это значит, что все элементы можно перебрать в цикле. Пример:
В результате выполнение в консоль выведется:
Понятная человеку семантика
Одна из причин, почему программисты предпочитают перечисления массивам, заключается в понятности.
Тип элемента перечисления — это перечисление
Хэшируемость
Элементы перечисления хэшируемые. То есть программист может использовать их в словарях и множествах. Вспомним, что хэш позволяет создавать высокопроизводительные структуры, используя хэш-функции для сокращения объема данных.
Создание методов
Программист может полностью вывести элемент перечисления, либо вывести его имя, либо вывести его значение. Для того чтобы упростить это, можно самостоятельно создать в перечислении метод. Пример:
Следует понимать, что это лишь маленький скрипт с перечислением из трёх элементов, в большой программе такой метод может очень значительно сократить код.
Распространённые ошибки
Работая с перечислениями программист может допустить следующие ошибки:
Гайд по использованию enum в Python
Модуль enum содержит в себе тип для перечисления значений с возможностью итерирования и сравнения. Его можно использовать для создания понятных обозначений вместо использования чисел (для которых приходится помнить, какое число что обозначает) или строк (в которых легко опечататься и не заметить).
Создание
Итерирование
При итерировании по классу вы пройдёте по атрибутам.
Цикл будет идти по элементам в том порядке, в котором они указаны при создании класса. Названия и значения никак не влияют на порядок.
Сравнение перечислений
Так как элементы перечислений не упорядочены, то они поддерживают сравнение только по названию или значению.
IntEnum
Уникальные значения в перечислениях
Элементы перечисления с одинаковыми значениями являются несколькими названиями, указывающими на один и тот же объект.
Так как by_design и closed являются синонимами для других элементов, то они не появляются как элементы в циклах. Истинным считается название, указанное первым при объявлении.
Если вы хотите, чтобы все элементы обязательно имели разные значения, то добавьте декоратор @unique перед объявлением класса.
Элементы с повторяющимися значениями будут вызывать ValueError во время интерпретации.
Другой способ создания перечислений
Иногда удобнее не хардкодить элементы перечисления, а указывать их в более удобном виде. Для этого можно передать значения в конструктор класса:
Аргумент value является названием перечисления, которое используется для создания представления элементов. Второй аргумент names принимает список названий в перечислении. Если подать одну строку, то она будет разбита по пробельным символам и запятым, а значения будут числами, начиная с 1.
Также аргумент name принимает список пар название — значение, либо аналогичный словарь.
Передача списка пар позволяет сохранить порядок элементов, аналогично случаю, где мы объявляли атрибуты.
Другие типы значений
В этом примере каждое значение является парой из числового id и списка строк, описывающих возможный переход из данного состояния.
Данный пример аналогичен предыдущему, но в нём используются словари вместо tuple для удобства.
Быстрый ENUM
Зачем нужно перечисление (enum)
(если вы все знаете — опуститесь до секции «Перечисления в стандартной библиотеке»)
Представьте, что вам нужно описать набор всех возможных состояний сущностей в собственной модели базы данных. Скорее всего, вы возьмёте пачку констант, определенных прямо в пространстве имен модуля:
… или как статические атрибуты класса:
Такой подход поможет сослаться на эти состояния по мнемоническим именам, в то время как в вашем хранилище они будут представлять собой обычные целые числа. Таким образом вы одновременно избавляетесь от магических чисел, разбросанных по разным участкам кода, заодно делая его более читабельным и информативным.
Или же ваш класс становится таким:
Наконец, именованный кортеж превращается в:
Уже неплохо — теперь он гарантирует, что и значение состояния и заглушка перевода отображаются на языки поддерживаемые UI. Но вы можете заметить, что код, использующий эти отображения, превратился в бардак. Каждый раз, пытаясь присвоить значение сущности, приходится извлекать значение с индексом 0 из используемого вами отображения:
илиили
И так далее. Помните, что первые два подхода, использующие константы и атрибуты класса, соответственно, страдают от изменяемости.
И вот перечисления приходят к нам на помощь
Вот и все. Теперь вы можете легко перебирать перечисление в вашем рендере (синтаксис Jinja2):
Перечисление является неизменяемым как для набора элементов — нельзя определить новый член перечисления во время выполнения и нельзя удалить уже определенный член, так и для тех значений элементов, которые он хранит — нельзя [пере]назначать любые значения атрибута или удалять атрибут.
В вашем коде вы просто присваиваете значения вашим сущностям, вот так:
Все достаточно понятно, информативно и расширяемо. Вот для чего мы используем перечисления.
Как мы смогли сделать его быстрее?
Перечисление из стандартной библиотеки довольно медленное, поэтому мы спросили себя — можем ли мы ускорить его? Как оказалось — можем, а именно, реализация нашего перечисления:
Slots
К примеру, можно использовать объявление класса с помощью __slots__ — в этом случае все экземпляры классов будут иметь только ограниченный набор свойств, объявленных в __slots__ и всех __slots__ родительских классов.
Descriptors
По умолчанию интерпретатор Python возвращает значение атрибута объекта напрямую (при этом оговоримся, что в данном случае значение — это тоже объект Python, а не, например, unsigned long long в терминах языка Си):
value = my_obj.attribute # это прямой доступ к значению атрибута по указателю, который объект хранит для этого атрибута.
Перечисления в стандартной библиотеке
Таким образом, вся последовательность вызовов может быть представлена следующими псевдокодом:
Мы написали простой скрипт демонстрирующий вывод, описанный выше:
И после выполнения скрипт выдал нам следующую картинку:
Сравните с нашим FastEnum:
Что видно на следующем изображении:
Все это действительно происходит внутри стандартной реализации перечислений каждый раз, когда вы обращаетесь к свойствам name и value их членов. Это же и причина, по которой наша реализация быстрее.
Наш подход
Свою реализацию перечислений мы создавали с оглядкой на элегантные перечисления в C и прекрасные расширяемые перечисления в Java. Основные функции, которые мы хотели реализовать у себя, были следующими:
Каковы дополнительные фишки?
FastEnum не совместим ни с какой версией Python до 3.6, поскольку повсеместно использует аннотации типов, внедренные в Python 3.6. Можно предположить, что установка модуля typing из PyPi поможет. Краткий ответ — нет. Реализация использует PEP-484 для аргументов некоторых функций, методов и указателей на тип возвращаемого значения, поэтому любая версия до Python 3.5 не поддерживается из-за несовместимости синтаксиса. Но, опять же, самая первая строка кода в __new__ метакласса использует синтаксис PEP-526 для указания типа переменной. Так что Python 3.5 тоже не подойдет. Можно перенести реализацию на более старые версии, хотя мы в Qrator Labs, как правило, используем аннотации типов когда это возможно, так как это сильно помогает в разработке сложных проектов. Ну и в конце-концов! Вы же не хотите застрять в Python до версии 3.6, поскольку в более новых версиях нет обратной несовместимости с вашим существующим кодом (при условии, что вы не используете Python 2), а ведь в реализации asyncio была проделана большая работа по сравнению с 3.5, на наш взгляд, стоящая незамедлительного обновления.
Однако, существуют некоторые ограничения: все имена членов перечисления должны быть написаны ЗАГЛАВНЫМИ буквами, иначе они не будут обработаны метаклассом.
Наконец, вы можете объявить базовый класс для ваших перечислений (имейте в виду, что базовый класс может сам использовать метакласс, поэтому вам не нужно предоставлять метакласс всем подклассам) — достаточно определить общую логику (атрибуты и методы) в этом классе и не определять членов перечисления (так что класс не будет «финализирован»). После можно объявить столько наследующих классов этого класса, сколько захотите, а сами наследники при этом будут иметь общую логику.
Псевдонимы и как они могут помочь
Предположим, что у вас есть код, использующий:
И что класс MyEnum объявлен следующим образом:
Теперь, вы решили что хотите сделать кое-какой рефакторинг и перенести перечисление в другой пакет. Вы создаете что-то вроде этого:
Где MyMovedEnum объявлен так:
Вот и все. При перезапуске ваших приложений на этапе unpickle все члены перечисления будут переобъявлены как экземпляры MyMovedEnum и станут связаны с этим новым классом. В тот момент, когда вы будете уверены, что все ваши хранимые, например, в базе данных, объекты были повторно десериализованы (и, возможно, сериализованы опять и сохранены в хранилище) — вы можете выпустить новый релиз, в котором ранее помеченный как устаревший класс MyEnum может быть объявлен более ненужным и удаленным из кодовой базы.
Примеры работы со списками Python
Список Python — это последовательность значений любого типа: строки, числа, числа с плавающей точкой или даже смешанного типа. В этом материале речь пойдет о функциях списков, о том, как создавать их, добавлять элементы, представлять в обратном порядке и многих других.
Создать списки Python
Для создания списка Python нужно заключить элементы в квадратные скобки:
Список может выглядеть так:
Можно смешивать типы содержимого:
Поддерживаются вложенные списки как в примере выше.
Получать доступ к любому элементу списка можно через его индекс. В Python используется система индексации, начиная с нуля.
Принцип похож на строки.
Изменение списка
Списки — это изменяемые объекты, поэтому их элементы могут изменяться, или же может меняться их порядок.
Если есть такой список:
То его третий элемент можно изменить следующим образом:
Если сейчас вывести его на экран, то он будет выглядеть вот так:
Если индекс — отрицательное число, то он будет считаться с последнего элемента.
Вывод этого кода — ‘пять’.
Проход (итерация) по списку
Читать элементы списка можно с помощью следующего цикла:
Таким образом можно читать элементы списка. А вот что касается их обновления:
Результат будет следующим:
Функция len() используется для возврата количества элементов, а range() — списка индексов.
Стоит запомнить, что вложенный список — это всегда один элемент вне зависимости от количества его элементов.
Срез списка
Можно получить срез списка с помощью оператора ( : ):
Результат кода выше — [‘два’, ‘три’]
Если убрать первое число, от срез будет начинаться с первого элемента, а если второе — с последнего.
Если убрать числа и оставить только двоеточие, то скопируется весь список.
Результат этого года:
Поскольку списки изменяемые, менять элементы можно с помощью оператора среза:
Вставить в список
Метод insert можно использовать, чтобы вставить элемент в список:
Индексы для вставляемых элементов также начинаются с нуля.
Добавить в список
Метод append можно использовать для добавления элемента в список:
Можно добавить и больше одного элемента таким способом:
При этом list_2 не поменяется.
Отсортировать список
Перевернуть список
Можно развернуть порядок элементов в списке с помощью метода reverse :
Индекс элемента
Метод index можно использовать для получения индекса элемента:
Если в списке больше одного такого же элемента, функция вернет индекс первого.
Удалить элемент
Удалить элемент можно, написав его индекс в методе pop :
Если не указывать индекс, то функция удалит последний элемент.
Оператор del можно использовать для тех же целей:
Можно удалить несколько элементов с помощью оператора среза:
Функции агрегации
В Python есть некоторые агрегатные функции:
sum() работает только с числовыми значениями.
Сравнить списки
В Python 2 сравнить элементы двух списком можно с помощью функции cmp :
В Python 3 для этого используется оператор ( == ):
Математические операции на списках:
Для объединения списков можно использовать оператор ( + ):
Список можно повторить с помощью оператора умножения:
Списки и строки
Для конвертации строки в набор символов, можно использовать функцию list :
Функция list используется для того, чтобы разбивать строку на отдельные символы.
Можно использовать метод split для разбития строки на слова:
Она возвращает обычный список, где с каждым словом можно взаимодействовать через индекс.
Символом разбития может служить любой знак, а не только пробел.
Результат будет аналогичен:
Объединить список в строку
Обратный процесс — объединение элементов списка в строку.
Это делается с помощью метода join :
Алиасинг (псевдонимы)
Когда две переменные ссылаются на один и тот же объект:
Алиасинг значит, что на объект ссылается больше одного имени.
Следующий пример показывает, как меняются изменяемые списки: