Код
#статьи

Бриллиант из коробки: Python-библиотека collections

Не просто библиотека, а та самая библиотека.

Язык программирования Python, по сути, состоит из библиотек. Каждую из них можно установить, а затем импортировать в свой код и использовать как вам нужно. Наиболее полезные включаются в установку по умолчанию — они появляются на вашем компьютере вместе с собственно интерпретатором Python.

Библиотека collections — одна из таких библиотек «из коробки». В ней содержатся мощные, удобные и быстродействующие альтернативы встроенным типам данных: словарям, спискам, кортежам.

Это может пригодиться в задачах подсчёта значений, организации сбора и быстрой конвертации данных из файлов CSV и баз SQL. Говорят, что разработчики и дата-сайентисты, открывшие для себя библиотеку collections, удивляются: «Как это я раньше жил без неё?»

Изображение: Киностудия «Мосфильм»


Цокто Жигмытов

С некоторых пор утверждает, что он data scientist. В предыдущих сезонах выдавал себя за математика, звукорежиссёра, радиоведущего, переводчика, писателя. Кандидат наук, но не точных. Бесстрашно пишет о Data Science и программировании на Python.


defaultdict: безошибочный словарь

Обычный словарь dict в Python содержит пары вида «ключ:значение». Значение из словаря можно запросить по ключу. В случае если запрашиваемого ключа в словаре нет, Python выдаст ошибку:

my_dict = {'a':1, 'b':2, 'c':3}
my_dict['a']
>>> 1

my_dict['d']
>>> Traceback (most recent call last):
>>> File "<pyshell#2>", line 1, in <module>
>>>	   my_dict['d']
>>> KeyError: 'd'

Поведение логичное, но не слишком удобное для работы. Словарь defaultdict из библиотеки collections поступает иначе: если запросить у такого продвинутого словаря ключ, которого в нём нет, он не обидится и не выкинет ошибку, а вместо этого:

  • создаст этот ключ;
  • создаст к нему значение, которое будет иметь тип, оговорённый по умолчанию («по дефолту» — отсюда, кстати, и название), — например, целое число или строку;
  • и — тадамм! — вернёт вам это значение.

Строго говоря, в Python существуют стандартные методы словарей .setdefault() и .get() со сходной функциональностью, но они сложнее в использовании — надо каждый раз указывать значение по умолчанию. А здесь мы сразу задаём тип для всех таких значений и далее просто обращаемся по ключу.

Как это работает:

from collections import defaultdict
my_new_dict = defaultdict(int, my_dict)
my_new_dict
>>> defaultdict(<class 'int'>, {'a': 1, 'b': 2, 'c': 3})
my_new_dict['d']
>>> 0
my_new_dict
>>> defaultdict(<class 'int'>, {'a': 1, 'b': 2, 'c': 3, 'd': 0})

В первой строчке импортировали модуль defaultdict из библиотеки collections.

Во второй — превратили наш предыдущий словарь my_dict в словарь нового типа my_new_dict: «int», стоящий первым в скобках, означает, что значениями, создаваемыми по умолчанию, будут целые числа, а конкретно число ноль.

Далее мы выводим наш my_new_dict, чтобы посмотреть, что в нём. Интерпретатор Python показывает нам:

  • тип словаря: defaultdict;
  • тип значений по умолчанию: int;
  • а также текущее содержимое нашего словаря. Ключа «d» в нём нет, как и в оригинальном словаре my_dict.

В следующей строке мы как раз и запрашиваем элемент с этим ключом. И, о чудо, my_new_dict выдаёт нам значение — оно равно 0.

Смотрим снова на содержимое нашего словаря и видим, что там появилась новая пара 'd': 0.

Вы наверняка догадались, что вместо int при создании словаря типа defaultdict можно писать любые стандартные типы данных: float, list, str — и даже другие словари dict. И новые элементы, создаваемые при первом обращении к ключу, будут именно этого типа: нули, пустые списки, строки и словари соответственно.

my_new_dict_2 = defaultdict(dict, my_dict)
my_new_dict_2
>>> defaultdict(<class 'dict'>, {'a': 1, 'b': 2, 'c': 3})
my_new_dict_2['e']
>>> {}
my_new_dict_2
>>> defaultdict(<class 'dict'>, {'a': 1, 'b': 2, 'c': 3, 'e': {}})

И, конечно, словарь defaultdict поддерживает все методы обычных питоновских словарей: .items(), .values(), .keys() и другие.

namedtuple: кортеж, только именованный

Кортеж (tuple, его часто называют «неизменяемым списком») в Python содержит некоторое количество элементов, к каждому из которых можно обратиться только по его индексу.

simple_tuple = (1, 'a', 123.4)
simple_tuple[1]
>>> 'a'

Для простых задач этого хватает, но как только число элементов в кортеже начинает расти, их вызов по индексу становится головной болью, потому что существенно снижает читаемость кода.

Здесь снова приходит на помощь библиотека collections, а конкретно её функция namedtuple. Это расширение над встроенными кортежами, которое даёт имя каждому элементу кортежа, а точнее — создаёт именованные поля, в которые мы, как в строку в таблице, складываем значения.

В итоге получаем что-то вроде неизменяемого (immutable) словаря, только гораздо легче читаемого.

from collections import namedtuple
fruit = namedtuple('fruit','number sort color')
apple = fruit(number=5, sort='Antonovka', color='red')
orange = fruit(number=7, sort='Navel Late', color='yellow')

Мы импортировали функцию namedtuple из collections. Затем создали конструктор именованных кортежей fruit, указав:

  • общее название тех кортежей, которые мы будем создавать ('fruit');
  • строку, содержащую имена полей именованного кортежа. Имена разделены в строке пробелом.

А после этого создали два именованных кортежа: apple и orange.

Теперь можно обращаться к элементам этих кортежей не по индексу, а по именам полей number, sort и color. Они являются атрибутами именованных кортежей, то есть используются через точку и без скобок.

apple.sort
>>> 'Antonovka'
orange.color
>>> 'yellow'
# обращение по индексам работает по-прежнему
apple[2]
>>> 'red'

Именованные кортежи обладают всеми свойствами простых кортежей, в том числе быстродействием и неизменяемостью, а также поддерживают их методы. В документации есть хорошие примеры использования.

Counter: посчитает всё

Counter — это разновидность словаря Python, созданная для подсчёта, что понятно из названия. Ключами в ней выступают подсчитываемые элементы, а значениями — их количества. Посмотрим на примерах, как она работает.

from collections import Counter


# строки
c = Counter('абвабббаввабббвавабг')
print(c)
>>> Counter({'б': 8, 'а': 6, 'в': 5, 'г': 1})

# списки
lst = [5,6,7,1,3,9,9,1,2,5,5,7,7]
c = Counter(lst)
print(c)
>>> Counter({5: 3, 7: 3, 1: 2, 9: 2, 6: 1, 3: 1, 2: 1})

Чаще всего в строке встречается буква «б» — 8 раз, а в списке — числа 5 и 7, по 3 раза. Подсчёт слов в предложении сводится к подсчёту элементов списка:

s = 'зима зима зима по полгода ни звоночка ни письма'
words = s.split()  # вернёт список слов
Counter(words)
>>> Counter({'зима': 3, 'ни': 2, 'по': 1, 'полгода': 1, 'звоночка': 1, 'письма': 1})

Counter поддерживает все методы словарей плюс дополнительные методы elements() и most_common(n). Первый выдаёт список из элементов, количество которых больше нуля, а второй выводит список из n пар самых частых элементов в виде кортежей (элемент, количество).

c = Counter(a=3, b=2, c=1, d=-2)  # указали количества элементов
sorted(c.elements())
>>> ['a', 'a', 'a', 'b', 'b', 'c']  

s = 'зима зима зима по полгода ни звоночка ни письма'
words = s.split()
Counter(words).most_common(2)  # 2 самых частых элемента и их количество
>>> [('зима', 3), ('ни', 2)]

Типовые примеры использования Counter():

sum(c.values())                 # сумма всех количеств
c.clear()                       # сброс всех количеств
list(c)                         # список элементов
set(c)                          # преобразование во множество
dict(c)                         # преобразование в обычный словарь 
c.items()                       # преобразование в список кортежей (элемент, количество)
Counter(dict(list_of_pairs))    # обратное преобразование из списка кортежей вида (элемент, количество)
c.most_common()[:-n-1:-1]       # n самых редких элементов
c += Counter()                  # удаление элементов с отрицательными и нулевыми количествами

Что дальше

Удобные инструменты из библиотеки collections помогут быстро разобраться с большим количеством однотипных данных: обработать их, преобразовать и передать дальше. Используйте collections везде, где требуется скорость работы, надёжный учёт данных и хорошая читаемость кода.

На курсе «Профессия Python-разработчик» вы изучите и другие бриллианты из многочисленного семейства библиотек Python. Все они помогут вам в учёбе и дальнейшем трудоустройстве. Приходите!


Курс

Профессия Python-разработчик

На Python пишут веб-приложения и нейросети, проводят научные вычисления и автоматизируют процессы. Язык просто выучить, даже если вы никогда не программировали. На курсе вы создадите Telegram-бота, полноценный магазин и задачник для портфолио, а Центр карьеры поможет найти работу Python-разработчиком.

Узнать про курс
Понравилась статья?
Да

Пользуясь нашим сайтом, вы соглашаетесь с тем, что мы используем cookies 🍪

Ссылка скопирована