Автор выбрал COVID-19 Relief Fund для получения пожертвования в рамках программы Write for DOnations.
В Python 3 имеется множество встроенных структур данных, включая кортежи, словари и списки. Структуры данных дают нам возможности организации и хранения данных. Модуль collections
помогает эффективно заполнять структуры данных и управлять ими.
В этом обучающем модуле мы рассмотрим три класса модуля collections
, которые помогут вам работать с кортежами, словарями и списками. Мы используем namedtuples
для создания кортежей с именованными полями, defaultdict
для удобного группирования информации в словарях и деки
для эффективного добавления элементов на любую сторону объекта в виде списка.
В этом обучающем модуле мы будем работать со списком рыб, изменяемым по мере добавления или удаления рыб из вымышленного аквариума.
Чтобы использовать этот обучающий модуль с наибольшей эффективностью, мы рекомендуем познакомиться с типами данных «кортеж», «словарь» и «список», их синтаксисом и способами извлечения данных из этих типов. Необходимую информацию можно получить, пройдя следующие обучающие модули:
Кортежи Python представляют собой неизменяемые упорядоченные последовательности элементов. Кортежи часто используются для представления табличных данных, например, строк из файла CSV или базы данных SQL. Аквариум может отслеживать список рыбок как серию кортежей.
Кортеж отдельной рыбки:
("Sammy", "shark", "tank-a")
Этот кортеж состоит из трех строковых элементов.
Хотя это полезно, данный массив четко не показывает, что означает каждое из полей. На самом деле элемент 0
— это имя, элемент 1
— это вид, а элемент 2
— резервуар.
Значение полей кортежа fish:
name | species | tank |
---|---|---|
Sammy | shark | tank-a |
В этой таблице понятно, что каждый из трех элементов кортежа имеет четкое значение.
namedtuple
из модуля collections
позволяет добавлять явные имена в каждый элемент кортежа, чтобы сделать их значения понятными в вашей программе Python.
Давайте используем namedtuple
для генерирования класса, который дает четкие имена каждому элементу кортежа fish:
from collections import namedtuple
Fish = namedtuple("Fish", ["name", "species", "tank"])
from collections import namedtuple
дает вашей программе Python доступ к функции фабрики namedtuple
. Вызов функции namedtuple()
возвращает класс, привязанный к имени Fish
. Функция namedtuple()
имеет два аргумента: желаемое имя нового класса "Fish"
и список именованных элементов ["name", "species", "tank"]
.
Мы можем использовать класс Fish
для представления описанного выше кортежа fish:
sammy = Fish("Sammy", "shark", "tank-a")
print(sammy)
Если мы выполним этот код, мы увидим следующие результаты:
OutputFish(name='Sammy', species='shark', tank='tank-a')
Создается экземпляр sammy
с использованием класса Fish
. sammy
— это кортеж из трех элементов с понятными именами.
Доступ к полям sammy
осуществляется по имени или через традиционный индекс кортежа:
print(sammy.species)
print(sammy[1])
Если мы выполним эти два вызова print
, результат будет выглядеть так:
Outputshark
shark
При доступе к.species
возвращается то же значение, что и при доступе ко второму элементу sammy
с использованием [1]
.
Использование namedtuple
из модуля collections
делает программу более удобочитаемой и при этом позволяет сохранить важные свойства кортежа (неизменяемость и упорядоченность).
Кроме того, функция фабрики namedtuple
добавляет несколько дополнительных методов в экземпляры Fish
.
Используйте ._asdict()
для конвертации экземпляра в словарь:
print(sammy._asdict())
Если мы выполним команду print
, результат будет выглядеть так:
Output{'name': 'Sammy', 'species': 'shark', 'tank': 'tank-a'}
При вызове .asdict()
для пользователя sammy
будет выведена схема словаря, где каждое из трех имен полей будет сопоставлено с соответствующим значением.
В версиях Python ниже 3.8 эта строка может выводиться немного по-другому. Например, вместо показанного здесь простого словаря вы можете увидеть OrderedDict
.
Примечание. В Python методы, начинающиеся с символа подчеркивания, обычно считаются «частными». При этом дополнительные методы, предоставляемые namedtuple
(_asdict()
, ._make()
, ._replace()
и т. д.) являются публичными.
Часто бывает полезно собирать данные в словарях Python. Словарь defaultdict
из модуля collections
может помочь быстро и кратко собрать информацию в словарях.
defaultdict
никогда не генерирует ошибку KeyError
. При отсутствии ключа defaultdict
просто вставляет и возвращает подстановочное значение:
from collections import defaultdict
my_defaultdict = defaultdict(list)
print(my_defaultdict["missing"])
Если запустить этот код, результат будет выглядеть следующим образом:
Output[]
defaultdict
вставляет и возвращает подстановочное значение вместо генерирования ошибки KeyError
. В этом случае мы задали подстановочное значение как список.
Обычные словари в подобных случаях выдают ошибку KeyError
при отсутствии ключей:
my_regular_dict = {}
my_regular_dict["missing"]
Если запустить этот код, результат будет выглядеть следующим образом:
OutputTraceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'missing'
Обычный словарь my_regular_dict
генерирует ошибку KeyError
при попытке доступа к отсутствующему ключу.
Поведение defaultdict
отличается от поведения обычного словаря. Вместо генерирования ошибки KeyError
при отсутствии ключа defaultdict
вызывает подстановочное значение без аргументов для создания нового объекта. В данном случае это list()
для создания пустого списка.
Продолжим рассмотрение нашего примера вымышленного аквариума. Допустим, у нас имеется список кортежей fish, представляющий содержимое аквариума:
fish_inventory = [
("Sammy", "shark", "tank-a"),
("Jamie", "cuttlefish", "tank-b"),
("Mary", "squid", "tank-a"),
]
В аквариуме три рыбки, и их названия, виды и резервуары указываются в этих трех кортежах.
Наша цель — организовать инвентарный список по резервуарам и получить список рыб, присутствующих в каждом резервуаре. Другими словами, нам нужен словарь, сопоставляющий "tank-a"
с ["Jamie", "Mary"]
и "tank-b"
с ["Jamie"]
.
Мы можем использовать defaultdict
для группировки рыб по резервуарам:
from collections import defaultdict
fish_inventory = [
("Sammy", "shark", "tank-a"),
("Jamie", "cuttlefish", "tank-b"),
("Mary", "squid", "tank-a"),
]
fish_names_by_tank = defaultdict(list)
for name, species, tank in fish_inventory:
fish_names_by_tank[tank].append(name)
print(fish_names_by_tank)
Запустив этот код, мы получим следующий результат:
Outputdefaultdict(<class 'list'>, {'tank-a': ['Sammy', 'Mary'], 'tank-b': ['Jamie']})
fish_names_by_tank
декларируется как словарь defaultdict
, который по умолчанию вставляет list()
вместо вывода ошибки KeyError
. Поскольку при этом каждый ключ в fish_names_by_tank
будет гарантированно указывать на список
, мы можем свободно вызывать .append()
для добавления имен в списки каждого резервуара.
В данном случае defaultdict
будет полезен, поскольку он сокращает вероятность непредвиденных ошибок KeyError
. Сокращение числа непредвиденных ошибок KeyError
означает, что программа будет содержать меньше строк и будет более понятной. В частности, идиома defaultdict
позволяет избежать создания экземпляров пустых списков для каждого резервуара вручную.
Без defaultdict
цикл for
выглядел бы примерно так:
...
fish_names_by_tank = {}
for name, species, tank in fish_inventory:
if tank not in fish_names_by_tank:
fish_names_by_tank[tank] = []
fish_names_by_tank[tank].append(name)
Если используется обычный словарь (вместо defaultdict
), цикл for
всегда должен проверять наличие определенного резервуара
в fish_names_by_tank
. Имя рыбки можно добавить только после подтверждения наличия резервуара
в fish_names_by_tank
или его инициализации с помощью []
.
defaultdict
помогает сократить объем кода при заполнении словарей, поскольку никогда не генерирует ошибки KeyError
.
Списки Python представляют собой изменяемые упорядоченные последовательности элементов. Python может добавлять элементы в списки за постоянное время (длина списка не влияет на время добавления), однако вставка в начало списка выполняется медленнее, и время такой вставки увеличивается с ростом размера списка.
С точки зрения нотации Big O добавление в список представляет собой операцию O(1)
с постоянным временем. Вставка в начало списка выполняется медленнее, с производительностью O(n)
.
Примечание. Инженеры-разработчики часто измеряют производительность процедур с помощью нотации «Big O». Если размер ввода не влияет на время выполнения процедуры, считается, что она выполняется за постоянное время или O(1)
(“Big O of 1”). Как уже говорилось выше, Python может добавлять элементы в списки за постоянное время или O(1)
.
Иногда размер ввода напрямую влияет на время выполнения процедуры. Например, вставка в начало списка Python выполняется тем медленнее, чем больше элементов содержится в списке. В нотации Big O для определения размера ввода используется буква n
. Это означает, что для добавления элементов в начало списка Python требуется «линейное время» или O(n)
(“Big O of n”).
В целом процедуры O(1)
быстрее процедур O(n)
.
Мы можем выполнить вставку в начало списка Python:
favorite_fish_list = ["Sammy", "Jamie", "Mary"]
# O(n) performance
favorite_fish_list.insert(0, "Alice")
print(favorite_fish_list)
Если мы выполним следующий код, результат будет выглядеть так:
Output['Alice', 'Sammy', 'Jamie', 'Mary']
Метод .insert(index, object)
в списке позволяет нам вставить "Alice"
в начало списка favorite_fish_list
. Следует отметить, что вставка в начало списка осуществляется с производительностью O(n)
. По мере увеличения длины списка favorite_fish_list
увеличивается и время вставки имени рыбки в начало списка.
дека
(двусторонняя очередь) из модуля collection
s — это объект списка, позволяющий вставлять элементы в начало или конец последовательности за постоянное время (O(1))
.
Вставьте элемент в начало деки
:
from collections import deque
favorite_fish_deque = deque(["Sammy", "Jamie", "Mary"])
# O(1) performance
favorite_fish_deque.appendleft("Alice")
print(favorite_fish_deque)
Выполнив этот код, мы получим следующий результат:
Outputdeque(['Alice', 'Sammy', 'Jamie', 'Mary'])
Мы можем создать экземпляр деки
, используя готовый набор элементов. В данном случае это список из трех имен любимых рыбок. Вызов метода favorite_fish_deque
appendleft
позволяет нам вставить элемент в начало набора с производительностью O(1)
. Производительность O(1)
означает, что время добавления элемента в начало favorite_fish_deque
не увеличивается, даже если favorite_fish_deque
содержит тысячи или миллионы элементов.
Примечание. Хотя дека
позволяет добавлять записи в начало последовательности более эффективно, чем список, дека
не выполняет все операции более эффективно, чем список. Например, доступ к случайному элементу деки
осуществляется с производительностью O(n)
, а доступ к случайному элементу списка — с производительностью O(1)
. Деки
используются, когда нам важно быстро вставлять и удалять элементы с любой стороны набора. Полное сравнение производительности по времени можно найти в вики-справочнике по Python.
Модуль collections
— мощный элемент стандартной библиотеки Python, делающий работу с данными более краткой и эффективной. В этом обучающем модуле мы рассказали о трех классах модуля collections
: namedtuple
, defaultdict
и deque
.
Теперь вы можете использовать документацию по модулю collection
, чтобы узнать больше о других доступных классах и утилитах. Чтобы узнать больше о Python в целом, пройдите нашу серию обучающих модулей «Программирование на Python 3».
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!