Содержание страницы
8.3. collections – Контейнерные типы данных¶collections – Container datatypes
Этот модуль реализует специализированные типы контейнеров, которые служат альтернативой встроенным контейнерам общего назначения Python: dict, list, set и tuple.
| namedtuple() | фабричная функция для создания подклассов кортежей с именованными полями |
| deque | контейнер, подобный списку, с быстрыми добавлением и извлечением с обоих концов |
| Counter | подкласс dict для подсчёта хэшируемых объектов |
| OrderedDict | подкласс dict, который запоминает порядок добавления записей |
| defaultdict | подкласс dict, который вызывает фабричную функцию для предоставления отсутствующих значений |
| UserDict | обёртка вокруг словарей для упрощения создания подклассов dict |
| UserList | обёртка вокруг списков для упрощения создания подклассов list |
| UserString | обёртка вокруг строк для упрощения создания подклассов str |
В дополнение к конкретным классам контейнеров модуль collections предоставляет ABC (абстрактные базовые классы), которые можно использовать для проверки, предоставляет ли класс определённый интерфейс, например, является ли он хешируемым или отображением.
8.3.1. Counter объекты¶Counter objects
Предоставляется инструмент счётчика для удобного и быстрого подсчёта. Например:
>>> # Подсчитать вхождения слов в списке
>>> cnt = Counter()
>>> for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']:
... cnt[word] += 1
>>> cnt
Counter({'blue': 3, 'red': 2, 'green': 1})
>>> # Найти десять самых распространённых слов в «Гамлете»
>>> import re
>>> words = re.findall('\w+', open('hamlet.txt').read().lower())
>>> Counter(words).most_common(10)
[('the', 1143), ('and', 966), ('to', 762), ('of', 669), ('i', 631),
('you', 554), ('a', 546), ('my', 514), ('hamlet', 471), ('in', 451)]
- class collections.Counter([iterable-or-mapping])¶
Counter – это подкласс dict для подсчёта хешируемых объектов. Это неупорядоченная коллекция, в которой элементы хранятся как ключи словаря, а их количества – как значения словаря. Количества могут быть любыми целыми числами, включая ноль и отрицательные значения. Класс Counter аналогичен мультимножествам (bags или multisets) в других языках.
Элементы подсчитываются из итерируемого объекта или инициализируются из другого отображения (или счётчика):
>>> c = Counter() # новый пустой счётчик >>> c = Counter('gallahad') # новый счётчик из итерируемого объекта >>> c = Counter({'red': 4, 'blue': 2}) # новый счётчик из отображения >>> c = Counter(cats=4, dogs=8) # новый счётчик из именованных аргументов
Объекты Counter имеют интерфейс словаря, за исключением того, что при отсутствии элемента они возвращают нулевой счёт вместо возбуждения KeyError:
>>> c = Counter(['eggs', 'ham']) >>> c['bacon'] # счёт отсутствующего элемента равен нулю 0
Установка счёта в ноль не удаляет элемент из счётчика. Используйте del для полного удаления:
>>> c['sausage'] = 0 # запись счётчика с нулевым значением >>> del c['sausage'] # del действительно удаляет запись
Новое в версии 3.1.
Объекты Counter поддерживают два метода, помимо тех, что доступны для всех словарей:
- elements()¶
Возвращает итератор по элементам, повторяя каждый столько раз, сколько указано в его счёте. Элементы возвращаются в произвольном порядке. Если счёт элемента меньше единицы, elements() игнорирует его.
>>> c = Counter(a=4, b=2, c=0, d=-2) >>> list(c.elements()) ['a', 'a', 'a', 'a', 'b', 'b']
- most_common([n])¶
Возвращает список из n наиболее часто встречающихся элементов и их количество, от самого частого к самому редкому. Если n не указан, most_common() возвращает все элементы счётчика. Элементы с одинаковым количеством упорядочиваются произвольно:
>>> Counter('abracadabra').most_common(3) [('a', 5), ('r', 2), ('b', 2)]
Для объектов Counter доступны обычные методы словаря, за исключением двух, которые работают иначе для счётчиков.
- update([iterable-or-mapping])¶
- Элементы подсчитываются из iterable или добавляются из другого mapping (или счётчика). Как dict.update(), но добавляет счёты вместо их замены. Кроме того, iterable должен быть последовательностью элементов, а не последовательностью пар (key, value).
Распространённые приёмы работы с объектами Counter:
sum(c.values()) # сумма всех значений
c.clear() # сбросить все значения
list(c) # список уникальных элементов
set(c) # преобразовать в множество
dict(c) # преобразовать в обычный словарь
c.items() # преобразовать в список пар (elem, cnt)
Counter(dict(list_of_pairs)) # создать из списка пар (элемент, кол-во)
c.most_common()[:-n:-1] # n наименее частых элементов
c += Counter() # удалить нулевые и отрицательные значения
Для объединения объектов Counter в мультимножества (счётчики, в которых счёты больше нуля) предусмотрено несколько математических операций. Сложение и вычитание объединяют счётчики путём сложения или вычитания счётов соответствующих элементов. Пересечение и объединение возвращают минимум и максимум соответствующих счётов. Каждая операция может принимать входные данные со знаковыми счётами, но на выходе будут исключены результаты со счётом ноль или меньше.
>>> c = Counter(a=3, b=1)
>>> d = Counter(a=1, b=2)
>>> c + d # сложение двух счётчиков: c[x] + d[x]
Counter({'a': 4, 'b': 3})
>>> c - d # вычитание (остаются только положительные значения)
Counter({'a': 2})
>>> c & d # пересечение: min(c[x], d[x])
Counter({'a': 1, 'b': 1})
>>> c | d # объединение: max(c[x], d[x])
Counter({'a': 3, 'b': 2})
Примечание
Счётчики в первую очередь предназначены для работы с положительными целыми числами для представления текущих счётчиков; однако была предпринята осторожность, чтобы излишне не исключать варианты использования, требующие других типов или отрицательных значений. Чтобы помочь с такими вариантами, в этом разделе описаны минимальные ограничения по диапазону и типам.
- Сам класс Counter является подклассом словаря без ограничений на ключи и значения. Значения предназначены для чисел, представляющих счёты, но в поле значения можно хранить что угодно.
- Метод most_common() требует только, чтобы значения были упорядочиваемыми.
- Для операций на месте, таких как c[key] += 1, тип значения должен поддерживать только сложение и вычитание. Так что дроби, числа с плавающей запятой и десятичные числа подойдут, а отрицательные значения поддерживаются. То же самое верно для update() и subtract(), которые допускают отрицательные и нулевые значения как на входе, так и на выходе.
- Методы для мультимножеств предназначены только для случаев использования с положительными значениями. Входные данные могут быть отрицательными или нулевыми, но создаются только выходные данные с положительными значениями. Типовые ограничения отсутствуют, но тип значения должен поддерживать сложение, вычитание и сравнение.
- Метод elements() требует целочисленных счётов. Он игнорирует нулевые и отрицательные счёты.
См. также
Класс Counter адаптирован для Python 2.5 и раннего рецепта Bag для Python 2.4.
Класс Bag в Smalltalk.
Статья в Википедии о мультимножествах.
Мультимножества в C++: учебное пособие с примерами.
О математических операциях над мультимножествами и их применении см. Кнут, Дональд. Искусство программирования, том II, раздел 4.6.3, упражнение 19.
Чтобы перечислить все различные мультимножества заданного размера из заданного набора элементов, см. itertools.combinations_with_replacement().
map(Counter, combinations_with_replacement(‘ABC’, 2)) –> AA AB AC BB BC CC
8.3.2. deque объекты¶deque objects
- class collections.deque([iterable[, maxlen]])¶
Возвращает новый объект deque, инициализированный слева направо (с помощью append()) данными из iterable. Если iterable не указан, новый deque пуст.
Deques представляют собой обобщение стеков и очередей (название произносится «дэк» и является сокращением от «двусторонняя очередь»). Deques поддерживают потокобезопасные и эффективные по памяти операции добавления и извлечения элементов с любого из концов очереди с примерно одинаковой производительностью O(1) в обоих направлениях.
Хотя объекты list поддерживают аналогичные операции, они оптимизированы для быстрых операций фиксированной длины и несут затраты памяти O(n) для операций pop(0) и insert(0, v), которые изменяют как размер, так и положение базового представления данных.
Если maxlen не указан или равен None, deque могут расти до произвольной длины. В противном случае deque ограничен заданной максимальной длиной. Как только deque с ограниченной длиной заполняется, при добавлении новых элементов соответствующее количество элементов отбрасывается с противоположного конца. Deque с ограниченной длиной предоставляют функциональность, аналогичную фильтру tail в Unix. Они также полезны для отслеживания транзакций и других пулов данных, где интерес представляет только самая последняя активность.
Объекты deque поддерживают следующие методы:
- append(x)¶
- Добавляет x в правую часть deque.
- appendleft(x)¶
- Добавляет x в левую часть deque.
- clear()¶
- Удаляет все элементы из deque, после чего его длина равна 0.
- extend(iterable)¶
- Расширяет правую часть deque, добавляя элементы из итерируемого объекта.
- extendleft(iterable)¶
- Расширяет левую часть deque, добавляя элементы из итерируемого объекта. Обратите внимание: последовательное добавление слева приводит к обращению порядка элементов в итерируемом объекте.
- pop()¶
- Удаляет и возвращает элемент с правой стороны deque. Если элементов нет, возбуждает IndexError.
- popleft()¶
- Удаляет и возвращает элемент с левой стороны deque. Если элементов нет, возбуждает IndexError.
- remove(value)¶
- Удаляет первое вхождение value. Если не найдено, возбуждает ValueError.
- rotate(n)¶
- Поворачивает deque на n шагов вправо. Если n отрицательно, поворачивает влево. Поворот на один шаг вправо эквивалентен: d.appendleft(d.pop()).
Объекты deque также предоставляют один атрибут только для чтения:
- maxlen¶
Максимальный размер deque или None, если неограничен.
Новое в версии 3.1.
В дополнение к вышеперечисленному, двусторонние очереди поддерживают итерацию, сериализацию, len(d), reversed(d), copy.copy(d), copy.deepcopy(d), проверку принадлежности с помощью оператора in и доступ по индексу, например d[-1]. Индексированный доступ занимает O(1) на обоих концах, но замедляется до O(n) в середине. Для быстрого произвольного доступа используйте списки.
Пример:
>>> from collections import deque
>>> d = deque('ghi') # создать новый deque с тремя элементами
>>> 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() # вернуть и удалить правый элемент
'j'
>>> d.popleft() # вернуть и удалить левый элемент
'f'
>>> list(d) # вывести содержимое deque
['g', 'h', 'i']
>>> d[0] # посмотреть левый элемент
'g'
>>> d[-1] # посмотреть правый элемент
'i'
>>> list(reversed(d)) # вывести содержимое deque в обратном порядке
['i', 'h', 'g']
>>> 'h' in d # поиск в deque
True
>>> d.extend('jkl') # добавить несколько элементов за раз
>>> d
deque(['g', 'h', 'i', 'j', 'k', 'l'])
>>> d.rotate(1) # поворот вправо
>>> d
deque(['l', 'g', 'h', 'i', 'j', 'k'])
>>> d.rotate(-1) # поворот влево
>>> d
deque(['g', 'h', 'i', 'j', 'k', 'l'])
>>> deque(reversed(d)) # создать новую deque в обратном порядке
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
deque(['c', 'b', 'a'])
8.3.2.1. deque Рецепты¶deque Recipes
В этом разделе рассматриваются различные подходы к работе с деками.
Двусторонние очереди с ограниченной длиной предоставляют функциональность, аналогичную фильтру tail в Unix:
def tail(filename, n=10):
'Return the last n lines of a file'
return deque(open(filename), n)
Другой подход к использованию деков заключается в поддержании последовательности недавно добавленных элементов путем добавления справа и извлечения слева:
def moving_average(iterable, n=3):
# moving_average([40, 30, 50, 46, 39, 44]) --> 40.0 42.0 45.0 43.0
# http://en.wikipedia.org/wiki/Moving_average
it = iter(iterable)
d = deque(itertools.islice(it, n-1))
d.appendleft(0)
s = sum(d)
for elem in it:
s += elem - d.popleft()
d.append(elem)
yield s / n
Метод rotate() предоставляет способ реализовать срезы и удаление для deque. Например, чистая реализация Python для del d[n] опирается на метод rotate() для позиционирования элементов, которые нужно извлечь:
def delete_nth(d, n):
d.rotate(-n)
d.popleft()
d.rotate(n)
Чтобы реализовать срезы для deque, используйте аналогичный подход, применяя rotate() для перемещения целевого элемента к левой стороне deque. Удалите старые записи с помощью popleft(), добавьте новые с помощью extend(), а затем обратите вращение. С небольшими вариациями этого подхода легко реализовать стековые манипуляции в стиле Forth, такие как dup, drop, swap, over, pick, rot и roll.
8.3.3. defaultdict объекты¶defaultdict objects
- class collections.defaultdict([default_factory[, ...]])¶
Возвращает новый объект, подобный словарю. defaultdict является подклассом встроенного класса dict. Он переопределяет один метод и добавляет одну изменяемую переменную экземпляра. Остальная функциональность такая же, как у класса dict, и здесь не документируется.
Первый аргумент задаёт начальное значение для атрибута default_factory; по умолчанию None. Все остальные аргументы обрабатываются так же, как если бы они были переданы конструктору dict, включая именованные аргументы.
Объекты defaultdict поддерживают следующий метод в дополнение к стандартным операциям dict:
- __missing__(key)¶
Если атрибут default_factory равен None, это возбуждает исключение KeyError с key в качестве аргумента.
Если default_factory не равен None, он вызывается без аргументов, чтобы предоставить значение по умолчанию для заданного key; это значение вставляется в словарь для key и возвращается.
Если вызов default_factory возбуждает исключение, это исключение распространяется без изменений.
Этот метод вызывается методом __getitem__() класса dict, когда запрошенный ключ не найден; то, что он возвращает или возбуждает, затем возвращается или возбуждается методом __getitem__().
Объекты defaultdict поддерживают следующую переменную экземпляра:
- default_factory¶
- Этот атрибут используется методом __missing__(); он инициализируется из первого аргумента конструктора, если он присутствует, или None, если отсутствует.
8.3.3.1. defaultdict Примеры¶defaultdict Examples
Используя list в качестве default_factory, легко сгруппировать последовательность пар ключ-значение в словарь списков:
>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> for k, v in s:
... d[k].append(v)
...
>>> list(d.items())
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
Когда ключ встречается впервые, его ещё нет в отображении; поэтому запись автоматически создаётся с помощью функции default_factory, которая возвращает пустой list. Затем операция list.append() добавляет значение в новый список. При повторной встрече ключа поиск происходит обычным образом (возвращается список для этого ключа), и операция list.append() добавляет ещё одно значение в список. Этот метод проще и быстрее, чем эквивалентный приём с dict.setdefault():
>>> d = {}
>>> for k, v in s:
... d.setdefault(k, []).append(v)
...
>>> list(d.items())
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
Установка default_factory в int делает defaultdict полезным для подсчёта (как мешок или мультимножество в других языках):
>>> s = 'mississippi'
>>> d = defaultdict(int)
>>> for k in s:
... d[k] += 1
...
>>> list(d.items())
[('i', 4), ('p', 2), ('s', 4), ('m', 1)]
Когда буква встречается впервые, она отсутствует в отображении, поэтому функция default_factory вызывает int(), чтобы получить нулевое значение по умолчанию. Затем операция инкремента наращивает счётчик для каждой буквы.
Функция int(), которая всегда возвращает ноль, – всего лишь частный случай константных функций. Более быстрый и гибкий способ создания константных функций – использовать лямбда-функцию, которая может возвращать любое константное значение (не только ноль):
>>> def constant_factory(value):
... return lambda: value
>>> d = defaultdict(constant_factory('<missing>'))
>>> d.update(name='John', action='ran')
>>> '%(name)s %(action)s to %(object)s' % d
'John ran to <missing>'
Установка default_factory в set делает defaultdict полезным для построения словаря множеств:
>>> s = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue', 4)]
>>> d = defaultdict(set)
>>> for k, v in s:
... d[k].add(v)
...
>>> list(d.items())
[('blue', set([2, 4])), ('red', set([1, 3]))]
8.3.4. namedtuple() Фабричная функция для кортежей с именованными полями¶namedtuple() Factory Function for Tuples with Named Fields
Именованные кортежи придают смысл каждой позиции в кортеже и позволяют писать более читаемый, самодокументируемый код. Их можно использовать везде, где используются обычные кортежи, и они добавляют возможность доступа к полям по имени, а не по индексу.
- collections.namedtuple(typename, field_names, verbose=False, rename=False)¶
Возвращает новый подкласс кортежа с именем typename. Новый подкласс используется для создания объектов, подобных кортежу, поля которых доступны через обращение к атрибутам, а также индексируемы и итерируемы. Экземпляры подкласса также имеют полезную docstring (с именем типа и полями) и полезный метод __repr__(), который выводит содержимое кортежа в формате name=value.
Параметр field_names может быть строкой, в которой имена полей разделены пробелами и/или запятыми, например 'x y' или 'x, y'. Как вариант, field_names может быть последовательностью строк, например ['x', 'y'].
В качестве имени поля может использоваться любой допустимый идентификатор Python, кроме имён, начинающихся с подчёркивания. Допустимые идентификаторы состоят из букв, цифр и подчёркиваний, но не начинаются с цифры или подчёркивания и не могут быть ключевым словом, таким как class, for, return, global, pass или raise.
Если rename равно true, недопустимые имена полей автоматически заменяются позиционными именами. Например, ['abc', 'def', 'ghi', 'abc'] преобразуется в ['abc', '_1', 'ghi', '_3'], удаляя ключевое слово def и дублирующееся имя поля abc.
Если verbose равно true, определение класса выводится непосредственно перед его созданием.
Экземпляры именованных кортежей не имеют собственных словарей, поэтому они легковесны и потребляют не больше памяти, чем обычные кортежи.
Изменено в версии 3.1: Добавлена поддержка rename.
Пример:
>>> Point = namedtuple('Point', 'x y', verbose=True)
class Point(tuple):
'Point(x, y)'
__slots__ = ()
_fields = ('x', 'y')
def __new__(_cls, x, y):
return _tuple.__new__(_cls, (x, y))
@classmethod
def _make(cls, iterable, new=tuple.__new__, len=len):
'Make a new Point object from a sequence or iterable'
result = new(cls, iterable)
if len(result) != 2:
raise TypeError('Expected 2 arguments, got %d' % len(result))
return result
def __repr__(self):
return 'Point(x=%r, y=%r)' % self
def _asdict(self):
'Return a new OrderedDict which maps field names to their values'
return OrderedDict(zip(self._fields, self))
def _replace(_self, **kwds):
'Return a new Point object replacing specified fields with new values'
result = _self._make(map(kwds.pop, ('x', 'y'), _self))
if kwds:
raise ValueError('Got unexpected field names: %r' % list(kwds.keys()))
return result
def __getnewargs__(self):
return tuple(self)
x = _property(_itemgetter(0))
y = _property(_itemgetter(1))
>>> p = Point(11, y=22) # создать экземпляр с позиционными или именованными аргументами
>>> p[0] + p[1] # доступен по индексу, как обычный кортеж (11, 22)
33
>>> x, y = p # распаковывается как обычный кортеж
>>> x, y
(11, 22)
>>> p.x + p.y # поля также доступны по имени
33
>>> p # читаемое __repr__ в стиле name=value
Point(x=11, y=22)
Именованные кортежи особенно полезны для присвоения имён полям кортежам результатов, возвращаемым модулями csv или sqlite3:
EmployeeRecord = namedtuple('EmployeeRecord', 'name, age, title, department, paygrade')
import csv
for emp in map(EmployeeRecord._make, csv.reader(open("employees.csv", "rb"))):
print(emp.name, emp.title)
import sqlite3
conn = sqlite3.connect('/companydata')
cursor = conn.cursor()
cursor.execute('SELECT name, age, title, department, paygrade FROM employees')
for emp in map(EmployeeRecord._make, cursor.fetchall()):
print(emp.name, emp.title)
В дополнение к методам, унаследованным от кортежей, именованные кортежи поддерживают три дополнительных метода и один атрибут. Чтобы избежать конфликтов с именами полей, имена методов и атрибутов начинаются с подчёркивания.
- classmethod somenamedtuple._make(iterable)¶
- Метод класса, который создаёт новый экземпляр из существующей последовательности или итерируемого объекта.
>>> t = [11, 22]
>>> Point._make(t)
Point(x=11, y=22)
- somenamedtuple._asdict()¶
Возвращает новый OrderedDict, который сопоставляет имена полей с соответствующими значениями:
>>> p._asdict() OrderedDict([('x', 11), ('y', 22)])
Изменено в версии 3.1: Возвращает OrderedDict вместо обычного dict.
- somenamedtuple._replace(kwargs)¶
- Возвращает новый экземпляр именованного кортежа, заменяя указанные поля новыми значениями:
>>> p = Point(x=11, y=22)
>>> p._replace(x=33)
Point(x=33, y=22)
>>> for partnum, record in inventory.items():
... inventory[partnum] = record._replace(price=newprices[partnum], timestamp=time.now())
- somenamedtuple._fields¶
- Кортеж строк с именами полей. Полезен для интроспекции и для создания новых типов именованных кортежей из существующих.
>>> p._fields # просмотреть имена полей
('x', 'y')
>>> Color = namedtuple('Color', 'red green blue')
>>> Pixel = namedtuple('Pixel', Point._fields + Color._fields)
>>> Pixel(11, 22, 128, 255, 0)
Pixel(x=11, y=22, red=128, green=255, blue=0)
Чтобы получить поле, имя которого хранится в строке, используйте функцию getattr():
>>> getattr(p, 'x')
11
Чтобы преобразовать словарь в именованный кортеж, используйте оператор двойной звёздочки (как описано в разделе Распаковка списков аргументов):
>>> d = {'x': 11, 'y': 22}
>>> Point(**d)
Point(x=11, y=22)
Поскольку именованный кортеж – это обычный класс Python, легко добавить или изменить функциональность с помощью подкласса. Вот как добавить вычисляемое поле и формат печати с фиксированной шириной:
>>> class Point(namedtuple('Point', 'x y')): ... __slots__ = () ... @property ... def hypot(self): ... return (self.x ** 2 + self.y ** 2) ** 0.5 ... def __str__(self): ... return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot)>>> for p in Point(3, 4), Point(14, 5/7): ... print(p) Point: x= 3.000 y= 4.000 hypot= 5.000 Point: x=14.000 y= 0.714 hypot=14.018
Приведённый выше подкласс устанавливает __slots__ в пустой кортеж. Это помогает снизить потребление памяти, предотвращая создание словарей экземпляров.
Наследование не подходит для добавления новых хранимых полей. Вместо этого просто создайте новый тип именованного кортежа на основе атрибута _fields:
>>> Point3D = namedtuple('Point3D', Point._fields + ('z',))
Значения по умолчанию можно реализовать, используя _replace() для настройки экземпляра-прототипа:
>>> Account = namedtuple('Account', 'owner balance transaction_count')
>>> default_account = Account('<owner name>', 0.0, 0)
>>> johns_account = default_account._replace(owner='John')
Перечислимые константы можно реализовать с помощью именованных кортежей, но проще и эффективнее использовать простое объявление класса:
>>> Status = namedtuple('Status', 'open pending closed')._make(range(3))
>>> Status.open, Status.pending, Status.closed
(0, 1, 2)
>>> class Status:
... open, pending, closed = range(3)
См. также
Рецепт именованного кортежа адаптирован для Python 2.4.
8.3.5. OrderedDict объекты¶OrderedDict objects
Упорядоченные словари работают как обычные словари, но запоминают порядок, в котором были добавлены элементы. При итерации по упорядоченному словарю элементы возвращаются в том порядке, в котором их ключи были впервые добавлены.
- class collections.OrderedDict([items])¶
Возвращает экземпляр подкласса dict, поддерживающий обычные методы dict. OrderedDict – это словарь, который запоминает порядок, в котором ключи были вставлены впервые. Если новая запись перезаписывает существующую, исходная позиция вставки остаётся неизменной. Удаление записи и повторная вставка перемещает её в конец.
Новое в версии 3.1.
В дополнение к обычным методам отображений, упорядоченные словари также поддерживают обратную итерацию с помощью reversed().
Проверки равенства между объектами OrderedDict учитывают порядок и реализуются как list(od1.items())==list(od2.items()). Проверки равенства между объектами OrderedDict и другими объектами Mapping не учитывают порядок, как обычные словари. Это позволяет заменять объекты OrderedDict везде, где используется обычный словарь.
Конструктор OrderedDict и метод update() оба принимают именованные аргументы, но их порядок теряется, поскольку семантика вызова функций Python передаёт именованные аргументы с помощью обычного неупорядоченного словаря.
См. также
Эквивалентный рецепт OrderedDict , работающий на Python 2.4 или новее.
8.3.5.1. OrderedDict Примеры и рецепты¶OrderedDict Examples and Recipes
Поскольку упорядоченный словарь запоминает порядок вставки, его можно использовать в сочетании с сортировкой для создания отсортированного словаря:
>>> # обычный неотсортированный словарь
>>> d = {'banana': 3, 'apple':4, 'pear': 1, 'orange': 2}
>>> # словарь, отсортированный по ключу
>>> OrderedDict(sorted(d.items(), key=lambda t: t[0]))
OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])
>>> # словарь, отсортированный по значению
>>> OrderedDict(sorted(d.items(), key=lambda t: t[1]))
OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])
>>> # словарь, отсортированный по длине строки ключа
>>> OrderedDict(sorted(d.items(), key=lambda t: len(t[0])))
OrderedDict([('pear', 1), ('apple', 4), ('orange', 2), ('banana', 3)])
Новые отсортированные словари сохраняют порядок сортировки при удалении записей. Но при добавлении новых ключей они добавляются в конец, и сортировка не сохраняется.
Также несложно создать вариант упорядоченного словаря, который запоминает порядок, в котором ключи были вставлены в последний раз. Если новая запись перезаписывает существующую, исходная позиция вставки изменяется и перемещается в конец:
class LastUpdatedOrderedDict(OrderedDict):
'Store items in the order the keys were last added'
def __setitem__(self, key, value):
if key in self:
del self[key]
OrderedDict.__setitem__(self, key, value)
Упорядоченный словарь можно объединить с классом Counter, чтобы счетчик запоминал порядок, в котором элементы встречаются впервые:
class OrderedCounter(Counter, OrderedDict):
'Counter that remembers the order elements are first encountered'
def __init__(self, iterable=None, **kwds):
OrderedDict.__init__(self)
Counter.__init__(self, iterable, **kwds)
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))
def __reduce__(self):
return self.__class__, (OrderedDict(self),)
8.3.6. UserDict объекты¶UserDict objects
Класс UserDict действует как обёртка вокруг объектов словаря. Необходимость в этом классе частично отпала благодаря возможности создавать подклассы напрямую от dict; однако с этим классом может быть проще работать, потому что базовый словарь доступен как атрибут.
- class collections.UserDict([initialdata])¶
Класс, имитирующий словарь. Содержимое экземпляра хранится в обычном словаре, который доступен через атрибут data объектов UserDict. Если передан initialdata, то data инициализируется его содержимым; обратите внимание, что ссылка на initialdata не сохраняется, что позволяет использовать его для других целей.
В дополнение к поддержке методов и операций отображений, экземпляры UserDict предоставляют следующий атрибут:
8.3.7. UserList объекты¶UserList objects
Этот класс действует как обёртка вокруг объектов-списков. Это полезный базовый класс для собственных классов, подобных спискам, которые могут наследовать от них и переопределять существующие методы или добавлять новые. Таким образом, можно добавлять новое поведение к спискам.
Необходимость в этом классе частично отпала благодаря возможности создавать подклассы напрямую от list; однако с этим классом может быть проще работать, потому что базовый список доступен как атрибут.
- class collections.UserList([list])¶
Класс, имитирующий список. Содержимое экземпляра хранится в обычном списке, который доступен через атрибут data объектов UserList. Содержимое экземпляра изначально устанавливается в копию list, по умолчанию пустой список []. list может быть любым итерируемым объектом, например, настоящим списком Python или объектом UserList.
В дополнение к поддержке методов и операций изменяемых последовательностей, экземпляры UserList предоставляют следующий атрибут:
Требования к наследованию: Подклассы UserList должны предоставлять конструктор, который можно вызывать без аргументов или с одним аргументом. Операции со списками, возвращающие новую последовательность, пытаются создать экземпляр фактического класса реализации. Для этого предполагается, что конструктор можно вызвать с одним параметром, который является объектом последовательности, используемым в качестве источника данных.
Если производный класс не желает соблюдать это требование, все специальные методы, поддерживаемые этим классом, должны быть переопределены; обратитесь к исходному коду за информацией о методах, которые необходимо предоставить в этом случае.
8.3.8. UserString объекты¶UserString objects
Класс UserString действует как обёртка вокруг строковых объектов. Необходимость в этом классе частично отпала благодаря возможности создавать подклассы напрямую от str; однако с этим классом может быть проще работать, потому что базовая строка доступна как атрибут.
- class collections.UserString([sequence])¶
- Класс, имитирующий строку или объект строки Unicode. Содержимое экземпляра хранится в обычном строковом объекте, который доступен через атрибут data объектов UserString. Содержимое экземпляра изначально устанавливается в копию sequence. sequence может быть экземпляром bytes, str, UserString (или подклассом) или произвольной последовательностью, которая может быть преобразована в строку с помощью встроенной функции str().
8.3.9. ABC – абстрактные базовые классы¶ABCs - abstract base classes
Модуль collections предоставляет следующие абстрактные базовые классы:
| ABC | Наследует от | Абстрактные методы | Методы-примеси |
|---|---|---|---|
| Container | __contains__ | ||
| Hashable | __hash__ | ||
| Iterable | __iter__ | ||
| Iterator | Iterable | __next__ | __iter__ |
| Sized | __len__ | ||
| Callable | __call__ | ||
| Sequence | Sized, Iterable, Container | __getitem__ | __contains__. __iter__, __reversed__, index, и count |
| MutableSequence | Sequence | __setitem__, __delitem__, insert | Унаследованные методы Sequence и append, reverse, extend, pop, remove и __iadd__ |
| Set | Sized, Iterable, Container | __le__, __lt__, __eq__, __ne__, __gt__, __ge__, __and__, __or__, __sub__, __xor__ и isdisjoint | |
| MutableSet | Set | add, discard | Унаследованные Set методы и clear, pop, remove, __ior__, __iand__, __ixor__ и __isub__ |
| Mapping | Sized, Iterable, Container | __getitem__ | __contains__, keys, items, values, get, __eq__ и __ne__ |
| MutableMapping | Mapping | __setitem__, __delitem__ | Унаследованные Mapping методы и pop, popitem, clear, update, и setdefault |
| MappingView | Sized | __len__ | |
| ItemsView | MappingView, Set | __contains__, __iter__ | |
| KeysView | MappingView, Set | __contains__, __iter__ | |
| ValuesView | MappingView | __contains__, __iter__ |
- class collections.Container¶
- class collections.Hashable¶
- class collections.Sized¶
- class collections.Callable¶
- Абстрактные базовые классы (ABC) для классов, предоставляющих соответственно методы __contains__(), __hash__(), __len__() и __call__().
- class collections.Iterable¶
- ABC для классов, предоставляющих метод __iter__(). См. также определение итерируемого объекта.
- class collections.Iterator¶
- Абстрактный базовый класс для классов, которые предоставляют методы __iter__() и next(). См. также определение итератора.
- class collections.Sequence¶
- class collections.MutableSequence¶
- ABC для неизменяемых и изменяемых последовательностей.
- class collections.Set¶
- class collections.MutableSet¶
- ABC для наборов только для чтения и изменяемых наборов.
- class collections.Mapping¶
- class collections.MutableMapping¶
- ABC для неизменяемых и изменяемых отображений.
- class collections.MappingView¶
- class collections.ItemsView¶
- class collections.KeysView¶
- class collections.ValuesView¶
- ABC для представлений отображений, элементов, ключей и значений.
Эти ABC позволяют проверять, предоставляют ли классы или их экземпляры определённую функциональность, например:
size = None
if isinstance(myvar, collections.Sized):
size = len(myvar)
Некоторые ABC также полезны в качестве примесей, упрощающих разработку классов, поддерживающих API контейнеров. Например, чтобы написать класс, поддерживающий полный API Set, достаточно предоставить три базовых абстрактных метода: __contains__(), __iter__() и __len__(). ABC предоставляет остальные методы, такие как __and__() и isdisjoint()
class ListBasedSet(collections.Set):
''' Альтернативная реализация множества, ориентированная на экономию памяти, а не на скорость
и не требующая, чтобы элементы множества были хэшируемыми. '''
def __init__(self, iterable):
self.elements = lst = []
for value in iterable:
if value not in lst:
lst.append(value)
def __iter__(self):
return iter(self.elements)
def __contains__(self, value):
return value in self.elements
def __len__(self):
return len(self.elements)
s1 = ListBasedSet('abcdef')
s2 = ListBasedSet('defghi')
overlap = s1 & s2 # Метод __and__() поддерживается автоматически
Замечания по использованию Set и MutableSet в качестве миксина:
- Поскольку некоторые операции над множествами создают новые множества, методам миксина по умолчанию требуется способ создавать новые экземпляры из итерируемого объекта. Предполагается, что конструктор класса имеет сигнатуру вида ClassName(iterable). Это предположение вынесено во внутренний метод класса _from_iterable(), который вызывает cls(iterable) для создания нового множества. Если миксин Set используется в классе с другой сигнатурой конструктора, потребуется переопределить _from_iterable() методом класса, который сможет создавать новые экземпляры из итерируемого аргумента.
- Чтобы переопределить операции сравнения (предположительно для скорости, так как семантика фиксирована), переопределите __le__(), и остальные операции автоматически последуют этому примеру.
- Примесь Set предоставляет метод _hash() для вычисления хеш-значения множества; однако __hash__() не определён, поскольку не все множества являются хешируемыми или неизменяемыми. Чтобы добавить хешируемость множеству с помощью примесей, унаследуйтесь от Set() и Hashable(), затем определите __hash__ = Set._hash.
См. также
- Рецепт OrderedSet для примера, построенного на основе MutableSet.
- Для получения дополнительной информации об ABC см. модуль abc и PEP 3119.