Модуль коллекций (collections)
Введение
Встроенный пакет коллекций предоставляет несколько специализированных, гибких типов коллекций, которые одновременно являются высокоэффективными и обеспечивают альтернативы общим типам коллекций dict
, list
, tuple
и set
. Модуль также определяет абстрактные базовые классы, описывающие различные типы функциональных возможностей коллекции (такие как MutableSet
и ItemsView
).
Замечания
В модуле коллекций доступны еще 3 типа, а именно:
- UserDict
- UserList
- UserString
Каждый из них действует как оболочка вокруг связанного объекта, например, UserDict
действует как оболочка вокруг объекта типа dict
. В каждом случае класс имитирует свой именованный тип. Содержимое экземпляра хранится в объекте обычного типа, доступ к которому осуществляется через атрибут data экземпляра-оболочки. В каждом из этих трех случаев потребность в этих типах была частично вытеснена способностью к подклассу непосредственно из базового типа.
collections.ChainMap
ChainMap
- новинка в версии 3.3. Возвращает новый ChainMap
объект с заданным числом карт. Этот объект группирует несколько словарей или других отображений вместе, чтобы создать единое обновляемое представление.
ChainMap
’ы полезны для управления вложенными контекстами и наложениями. Пример в мире Python находится в реализации Context
класса в шаблонном движке Джанго. Это полезно для быстрого связывания нескольких отображений, чтобы результат можно было рассматривать как единое целое. Это часто намного быстрее , чем создание нового словаря и запуск несколько update()
вызовов.
В любое время, когда у вас имеется цепочка значений поиска, может быть случай для ChainMap
. Пример включает в себя наличие заданных пользователем значений и словаря значений по умолчанию. Другим примером являются карты параметров POST
и GET
найденные в веб - использовании, например, Django или Flask. Благодаря использованию ChainMap
можно получить комбинированный представление двух различных словарей.
Список параметров maps
организуется от первого-поискового до последнего-поискового. Поисковые запросы последовательно выполняют поиск соответствующих сопоставления, пока не будет найден ключ. Напротив, операции записи, обновления и удаления действуют только при первом сопоставлении.
import collections
# определите 2 словаря с перекрывающимися хотя бы несколькими ключами
dict1 = {'apple': 1, 'banana': 2}
dict2 = {'coconut': 1, 'date': 1, 'apple': 3}
# создайте 2 ChainMap’а с разным порядком этих словарей.
combined_dict = collections.ChainMap(dict1, dict2)
reverse_ordered_dict = collections.ChainMap(dict2, dict1)
Обратите внимание на влияние порядка, в котором значение найдено первым в последующем поиске:
for k, v in combined_dict.items():
print(k, v)
>>>Out: coconut 1
date 1
apple 1
banana 2
for k, v in reverse_ordered_dict.items():
print(k, v)
>>>Out: apple 3
banana 2
coconut 1
collections.Counter
Счетчик является подклассом словаря , что позволяет легко считать объекты. У него есть полезные методы для работы с частотностью объектов, которые вы считаете.
import collections
counts = collections.Counter([1,2,3])
Приведенный выше код создает объект count, который имеет частоты всех элементов, передаваемых в конструктор. Этот пример имеет значение Counter({1: 1, 2: 1, 3: 1})
Примеры конструктора
Счетчик символов
collections.Counter('Happy Birthday')
>>>Out: Counter({'H': 1,
'a': 2,
'p': 2,
'y': 2,
' ': 1,
'B': 1,
'i': 1,
'r': 1,
't': 1,
'h': 1,
'd': 1})
Счетчик слов
collections.Counter('I am Sam Sam I am That Sam-I-am That Sam-I-am! I do not like that Sam-I-am'.split())
>>>Out: Counter({'I': 3,
'am': 2,
'Sam': 2,
'That': 2,
'Sam-I-am': 2,
'Sam-I-am!': 1,
'do': 1,
'not': 1,
'like': 1,
'that': 1})
Рецепты
c = collections.Counter({'a': 4, 'b': 2, 'c': -2, 'd': 0})
Получить количество отдельных элементов
c['a']
>>>Out: 4
Установить количество отдельных элементов
c['c'] = -3
c
>>>Out: Counter({'a': 4, 'b': 2, 'd': 0, 'c': -3})
Получить общее количество элементов в счетчике (4 + 2 + 0 - 3)
sum(c.values()) #отрицательные числа считаются!
>>>Out: 3
Получить элементы (сохраняются только элементы с положительным счетчиком)
list(c.elements())
>>>Out: ['a', 'a', 'a', 'a', 'b', 'b']
Удалить ключи с 0 или отрицательным значением
c - collections.Counter()
>>>Counter({'a': 4, 'b': 2})
Удалить все
c.clear()
c
>>>Out: Counter()
Добавить удалить отдельные элементы
c.update({'a': 3, 'b':3})
c.update({'a': 2, 'c':2}) # добавление к существующему, устанавливает если не существует
c
>>>Out: Counter({'a': 5, 'b': 3, 'c': 2})
c.subtract({'a': 3, 'b': 3, 'c': 3}) # вычитание (неготивные значения разрешены)
c
>>>Out: Counter({'a': 2, 'b': 0, 'c': -1})
collections.defaultdict
collections.defaultdict (default_factory)
возвращает подкласс dict
, который имеет значение по умолчанию для отсутствующих ключей. Аргумент должен быть функцией, которая возвращает значение по умолчанию при вызове без аргументов. Если нет ничего прошло, по умолчанию None
.
state_capitals = collections.defaultdict(str)
state_capitals
>>>Out: defaultdict(str, {})
возвращает ссылку на defaultdict, который создаст строковый объект с его методом default_factory.
Типичное использование defaultdict
это использовать один из встроенных типов , такие как str
, int
, list
или dict
как default_factory, так как они возвращают пустые типы при вызове без аргументов:
str()
>>>Out: ''
int()
>>>Out: 0
list()
>>>Out: []
Вызов defaultdict с ключом, который не существует, не приводит к ошибке, как в обычном словаре.
state_capitals['Alaska']
>>>Out: ''
state_capitals
>>>Out: defaultdict(str, {'Alaska': ''})
Другой пример с int
:
fruit_counts = collections.defaultdict(int)
fruit_counts['apple'] += 2 # No errors should occur
fruit_counts
>>>Out: defaultdict(int, {'apple': 2})
fruit_counts['banana'] # No errors should occur
>>>Out: 0
fruit_counts # A new key is created
>>>Out: default_dict(int, {'apple': 2, 'banana': 0})
Обычные словарные методы работают со словарем по умолчанию
>>> state_capitals['Alabama'] = 'Montgomery'
>>> state_capitals
defaultdict(<class 'str'>, {'Alabama': 'Montgomery', 'Alaska': ''})
Использование list
в качестве default_factory создаст список для каждого нового ключа.
>>> s = [('NC', 'Raleigh'),('VA', 'Richmond'),('WA', 'Seattle'),('NC', 'Asheville')]
>>> dd = collections.defaultdict(list)
>>> for k, v in s:
... dd[k].append(v)
>>> dd
defaultdict(<class 'list'>,
{'VA': ['Richmond'],
'NC': ['Raleigh', 'Asheville'],
'WA': ['Seattle']})
collections.deque
Возвращает новый deque
объект, который инициализируется слева направо (( используя append()
) с данными Iterable. Если итератор не указан, новый deque
пуст.
Deques - это обобщение стеков и очередей (название произносится как «deck» и сокращенно означает «двусторонняя очередь»). Двусторонних очереди поддерживает потокобезопасность, память эффективно добавляет и извлекает с обеих сторон deque
с приблизительно таким же O (1) производительностью в любом направлении
Хотя объекты списка поддерживают аналогичные операции, они оптимизированы для быстрых операций фиксированной длины и требуют O (n) затрат на перемещение памяти для операций pop (0) и insert (0, v), которые изменяют как размер, так и позицию базового представления данных.
Если maxlen
не указаны или None
, двусторонние очереди могут вырасти до произвольной длины. В противном случае, deque
ограничена до заданной максимальной длины. После того, как ограниченная длина deque
полна, когда добавляются новые элементы, соответствующее количество элементов, отбрасываются с противоположного конца. Запросы ограниченной длины обеспечивают функциональность, аналогичную хвостовому фильтру в Unix. Они также полезны для отслеживания транзакций и других пулов данных, где интерес представляют только самые последние действия.
from collections import deque
d = deque('ghi') # создаем новый deque с 3мя элементами
for elem in d: # итерируем элементы deque
print elem.upper()
G
H
I
d.append('j') # добавляем новую запись в правую
d.appendleft('f') # добавляем новую запись в левую часть
d # показываем представление deque
deque(['f', 'g', 'h', 'i', 'j'])
d.pop() # возвращаем и удаляем самый правый
>>>Out: 'j'
d.popleft() # возвращаем и удаляем самый левый элемент
>>>Out: 'f'
list(d) # перечисляем содержимое deque
>>>Out: ['g', 'h', 'i']
d[0] # смотрим самый левый элемент
>>>Out: 'g'
d[-1] # смотрим самый правый элемент
>>>Out: 'i'
list(reversed(d)) # перечисляем содержимое deque в # обратном порядке
>>>Out: ['i', 'h', 'g']
'h' in d # ищем deque
>>>Out: True
d.extend('jkl') # добавляем множественные элементы за раз
d
>>>Out: deque(['g', 'h', 'i', 'j', 'k', 'l'])
d.rotate(1) # правый поворот
d
>>>Out: deque(['l', 'g', 'h', 'i', 'j', 'k'])
d.rotate(-1) # левый поворот
d
>>>Out: deque(['g', 'h', 'i', 'j', 'k', 'l'])
deque(reversed(d)) # создаем новый deque в обратном
>>>Out: deque(['l', 'k', 'j', 'i', 'h', 'g'])
d.clear() # опустошаем deque
d.pop() # нельзя извлекать элемент из пустого # deque
Traceback (most recent call last):
File "<pyshell#6>", line 1, in -toplevel-
d.pop()
IndexError: pop from an empty deque
d.extendleft('abc') # extendleft() переворачивает порядок # ввода
d
>>>Out: deque(['c', 'b', 'a'])
Источник: https://docs.python.org/2/library/collections.html
collections.namedtuple
Определим нового типа Person
с использованием namedtuple
, как в пример ниже:
Person = namedtuple('Person', ['age', 'height', 'name'])
Второй аргумент - это список атрибутов, которые будет иметь кортеж. Вы можете перечислить эти атрибуты также в виде строки, разделенной пробелами или запятыми:
Person = namedtuple('Person', 'age, height, name')
или же
Person = namedtuple('Person', 'age height name')
После определения именованного кортежа можно создать экземпляр, вызвав объект с необходимыми параметрами, например:
dave = Person(30, 178, 'Dave')
Именованные аргументы также могут быть использованы:
jack = Person(age=30, height=178, name='Jack S.')
Теперь вы можете получить доступ к атрибутам namedtuple:
print(jack.age)
>>>Out: 30
print(jack.name)
>>>Out: 'Jack S.'
Первый аргумент namedtuple конструктора (в нашем примере 'Person'
) является typename
.Типично используют одно и то же слово для конструктора и имя типа, но они могут быть разными:
Human = namedtuple('Person', 'age, height, name')
dave = Human(30, 178, 'Dave')
print(dave) # возвращает объект класса Person
>>>Out: Person(age=30, height=178, name='Dave')
collections.OrderedDict
Порядок ключей в словарях Python произвольный: они не регулируются порядком их добавления.
Например:
d = {'foo': 5, 'bar': 6}
print(d)
>>>Out: {'foo': 5, 'bar': 6}
d['baz'] = 7
print(d)
>>>Out: {'baz': 7, 'foo': 5, 'bar': 6}
d['foobar'] = 8
print(d)
>>>Out: {'baz': 7, 'foo': 5, 'bar': 6, 'foobar': 8}
(Произвольный порядок, подразумеваемый выше, означает, что вы можете получить результаты отличные от результатов показанных выше в примере)
Порядок , в котором появляются ключи - порядок , в котором они будут повторяться, например , с использованием цикла for
.
collections.OrderedDict
класс предоставляет объекты словаря , которые сохраняют порядок ключей. OrderedDict
s могут быть созданы , как показано ниже , с серией упорядоченных элементов (здесь, список пар кортежей ключ-значение):
from collections import OrderedDict
d = OrderedDict([('foo', 5),('bar', 6)])
print(d)
>>>Out: OrderedDict([('foo', 5),('bar', 6)])
d['baz'] = 7
print(d)
>>>Out: OrderedDict([('foo', 5),('bar', 6),('baz', 7)])
d['foobar'] = 8
print(d)
>>>Out: OrderedDict([('foo', 5),('bar', 6),('baz', 7),('foobar', 8)])
Или мы можем создать пустой OrderedDict
, а затем добавить элементы:
o = OrderedDict()
o['key1'] = "value1"
o['key2'] = "value2"
print(o)
>>>Out: OrderedDict([('key1', 'value1'),('key2', 'value2')])
Итерация через OrderedDict
позволяет доступ к ключам в порядке их добавления.
Что произойдет, если мы присвоим новое значение существующему ключу?
d['foo'] = 4
print(d)
>>>Out: OrderedDict([('foo', 4),('bar', 6),('baz', 7),('foobar', 8)])
Ключ сохраняет свое первоначальное место в OrderedDict
.