Содержание страницы
8.3. collections – Высокопроизводительные контейнерные типы данных¶collections – High-performance container datatypes
Новое в версии 2.4.
Исходный код: Lib/collections.py и Lib/_abcoll.py
Этот модуль предоставляет специализированные типы данных-контейнеры в качестве альтернативы
встроенным контейнерам общего назначения Python: dict, list,
set и tuple.
фабричная функция для создания подклассов кортежей с именованными полями |
Новое в версии 2.6. |
|
контейнер, подобный списку, с быстрыми добавлением и извлечением с обоих концов |
Новое в версии 2.4. |
|
подкласс dict для подсчёта хэшируемых объектов |
Новое в версии 2.7. |
|
подкласс dict, который запоминает порядок добавления записей |
Новое в версии 2.7. |
|
подкласс dict, который вызывает фабричную функцию для предоставления отсутствующих значений |
Новое в версии 2.5. |
В дополнение к конкретным классам-контейнерам, модуль collections предоставляет абстрактные базовые классы, которые можно использовать для проверки того, предоставляет ли класс определённый интерфейс, например, является ли он хешируемым или отображением.
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(r'\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похож на мешки или мультимножества в других языках.Элементы подсчитываются из итерируемого объекта или инициализируются из другого отображения (или счётчика):
>>> 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 действительно удаляет запись
Новое в версии 2.7.
Объекты 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 опущено или
None,most_common()возвращает все элементы счётчика. Элементы с одинаковыми количествами упорядочиваются произвольно:>>> Counter('abracadabra').most_common(3) [('a', 5), ('r', 2), ('b', 2)]
-
subtract([iterable-or-mapping])¶ Элементы вычитаются из итерируемого объекта или из другого отображения (или счётчика). Подобно
dict.update(), но вычитает количества вместо замены. Как входные, так и выходные значения могут быть нулевыми или отрицательными.>>> c = Counter(a=4, b=2, c=0, d=-2) >>> d = Counter(a=1, b=2, c=3, d=4) >>> c.subtract(d) >>> c Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})
Обычные методы словарей доступны для объектов
Counter, за исключением двух, которые работают с счётчиками иначе.-
update([iterable-or-mapping])¶ Элементы подсчитываются из итерируемого объекта или добавляются из другого отображения (или счётчика). Как и
dict.update(), но увеличивает счётчики вместо их замены. Кроме того, итерируемый объект должен быть последовательностью элементов, а не последовательностью(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:-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()) данными из итерируемого объекта. Если итерируемый объект не указан, новый deque пуст.Deques представляют собой обобщение стеков и очередей (название произносится «дэк» и является сокращением от «двусторонняя очередь»). Deques поддерживают потокобезопасные и эффективные по памяти операции добавления и извлечения элементов с любого из концов очереди с примерно одинаковой производительностью O(1) в обоих направлениях.
Хотя объекты
listподдерживают аналогичные операции, они оптимизированы для быстрых операций фиксированной длины и влекут за собой затраты на перемещение памяти O(n) для операцийpop(0)иinsert(0, v), которые изменяют как размер, так и положение базового представления данных.Новое в версии 2.4.
Если maxlen не указан или равен
None, deques могут расти до произвольной длины. В противном случае deque ограничен заданной максимальной длиной. Как только deque ограниченной длины заполняется, при добавлении новых элементов соответствующее количество элементов отбрасывается с противоположного конца. Deque с ограниченной длиной предоставляют функциональность, аналогичную фильтруtailв Unix. Они также полезны для отслеживания транзакций и других пулов данных, где интерес представляет только самая последняя активность.Изменено в версии 2.6: Добавлен параметр maxlen.
Объекты deque поддерживают следующие методы:
-
append(x)¶ Добавляет x в правую часть deque.
-
appendleft(x)¶ Добавляет x в левую часть deque.
-
clear()¶ Удаляет все элементы из deque, после чего его длина равна 0.
-
count(x)¶ Подсчитывает количество элементов deque, равных x.
Новое в версии 2.7.
-
extend(iterable)¶ Расширяет правую часть deque, добавляя элементы из итерируемого объекта.
-
extendleft(iterable)¶ Расширяет левую часть deque, добавляя элементы из итерируемого объекта. Обратите внимание: последовательное добавление слева приводит к обращению порядка элементов в итерируемом объекте.
-
pop()¶ Удаляет и возвращает элемент из правой части deque. Если элемент отсутствует, вызывает исключение
IndexError.
-
popleft()¶ Удаляет и возвращает элемент из левой части deque. Если элемент отсутствует, вызывает исключение
IndexError.
-
remove(value)¶ Удаляет первое вхождение значения. Если не найдено, вызывает исключение
ValueError.Новое в версии 2.5.
-
reverse()¶ Переворачивает элементы deque на месте и возвращает
None.Новое в версии 2.7.
-
rotate(n=1)¶ Поворачивает deque на n шагов вправо. Если n отрицательно, выполняет поворот влево.
Когда deque не пуст, поворот на один шаг вправо эквивалентен
d.appendleft(d.pop()), а поворот на один шаг влево –d.append(d.popleft()).
Объекты deque также предоставляют один атрибут только для чтения:
-
maxlen¶ Максимальный размер дека или
None, если неограничен.Новое в версии 2.7.
-
В дополнение к вышеперечисленному, двусторонние очереди поддерживают итерацию, сериализацию, 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 / float(n)
Метод rotate() позволяет реализовать deque срезы и удаление. Например, чистая реализация del d[n] на Python использует метод rotate() для позиционирования элементов, которые нужно извлечь:
def delete_nth(d, n):
d.rotate(-n)
d.popleft()
d.rotate(n)
To implement deque slicing, use a similar approach applying
rotate() to bring a target element to the left side of the deque. Remove
old entries with popleft(), add new entries with extend(), and then
reverse the rotation.
With minor variations on that approach, it is easy to implement Forth style
stack manipulations such as dup, drop, swap, over, pick,
rot, and roll.
8.3.3. defaultdict объекты¶defaultdict objects
-
class
collections.defaultdict([default_factory[, ...]])¶ Возвращает новый объект, похожий на словарь.
defaultdictявляется подклассом встроенного классаdict. Он переопределяет один метод и добавляет одну изменяемую переменную экземпляра. Остальная функциональность такая же, как у классаdict, и здесь не описывается.Первый аргумент задает начальное значение для атрибута
default_factory; по умолчанию он равенNone. Все остальные аргументы обрабатываются так же, как если бы они были переданы конструкторуdict, включая именованные аргументы.Новое в версии 2.5.
defaultdictобъекты поддерживают следующий метод в дополнение к стандартным операциямdict:-
__missing__(key)¶ Если атрибут
default_factoryравенNone, возникает исключениеKeyErrorс аргументом key.Если
default_factoryне равенNone, он вызывается без аргументов для предоставления значения по умолчанию для заданного key; это значение вставляется в словарь для key и возвращается.Если вызов
default_factoryвызывает исключение, это исключение распространяется без изменений.Этот метод вызывается методом
__getitem__()классаdict, когда запрошенный ключ не найден; все, что он возвращает или возбуждает, затем возвращается или возбуждается методом__getitem__().Обратите внимание, что
__missing__()не вызывается ни для каких операций, кроме__getitem__(). Это означает, чтоget()будет, как обычные словари, возвращатьNoneпо умолчанию, а не использоватьdefault_factory.
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)
...
>>> 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)
...
>>> d.items()
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
Установка default_factory в int делает
defaultdict удобным для подсчёта (как bag или мультимножество в других
языках):
>>> s = 'mississippi'
>>> d = defaultdict(int)
>>> for k in s:
... d[k] += 1
...
>>> d.items()
[('i', 4), ('p', 2), ('s', 4), ('m', 1)]
Когда буква встречается впервые, в отображении она отсутствует, поэтому
функция default_factory вызывает int() для получения нулевого значения по умолчанию.
Затем операция инкремента наращивает счётчик для каждой буквы.
Функция int(), которая всегда возвращает ноль, является лишь частным случаем константных функций. Более быстрый и гибкий способ создания константных функций – использование itertools.repeat(), который может предоставлять любое константное значение (не только ноль):
>>> def constant_factory(value):
... return itertools.repeat(value).next
>>> 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)
...
>>> 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']. В качестве альтернативы field_names может быть единой строкой, в которой имена полей разделены пробелами и/или запятыми, например'x y'или'x, y'.Any valid Python identifier may be used for a fieldname except for names starting with an underscore. Valid identifiers consist of letters, digits, and underscores but do not start with a digit or underscore and cannot be a
keywordsuch as class, for, return, global, pass, print, or raise.Если rename равен true, недопустимые имена полей автоматически заменяются позиционными именами. Например,
['abc', 'def', 'ghi', 'abc']преобразуется в['abc', '_1', 'ghi', '_3'], устраняя ключевое словоdefи дублирующееся имя поляabc.Если verbose равно true, определение класса выводится непосредственно перед его созданием.
Экземпляры именованных кортежей не имеют собственных словарей, поэтому они легковесны и потребляют не больше памяти, чем обычные кортежи.
Новое в версии 2.6.
Изменено в версии 2.7: добавлена поддержка rename.
Пример:
>>> Point = namedtuple('Point', ['x', 'y'], verbose=True)
class Point(tuple):
'Point(x, y)'
__slots__ = ()
_fields = ('x', 'y')
def __new__(_cls, x, y):
'Create new instance of Point(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 a nicely formatted representation string'
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' % kwds.keys())
return result
def __getnewargs__(self):
'Return self as a plain tuple. Used by copy and pickle.'
return tuple(self)
__dict__ = _property(_asdict)
def __getstate__(self):
'Exclude the OrderedDict from pickling'
pass
x = _property(_itemgetter(0), doc='Alias for field number 0')
y = _property(_itemgetter(1), doc='Alias for field number 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 = Point(x=11, y=22) >>> p._asdict() OrderedDict([('x', 11), ('y', 22)])
Изменено в версии 2.7: Возвращает
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 – это словарь, который запоминает порядок, в котором ключи были впервые вставлены. Если новая запись перезаписывает существующую, исходная позиция вставки не изменяется. Удаление записи и повторная вставка переместит её в конец.Новое в версии 2.7.
-
OrderedDict.popitem(last=True)¶ Метод
popitem()для упорядоченных словарей возвращает и удаляет пару (ключ, значение). Пары возвращаются в порядке LIFO, если last равно true, или в порядке FIFO, если false.
Помимо обычных методов отображений, упорядоченные словари также поддерживают
обратную итерацию с помощью 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 __repr__(self):
return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))
def __reduce__(self):
return self.__class__, (OrderedDict(self),)
8.3.6. Абстрактные базовые классы коллекций¶Collections Abstract Base Classes
Модуль collections предоставляет следующие абстрактные базовые классы:
ABC |
Наследует от |
Абстрактные методы |
Методы-примеси |
|---|---|---|---|
|
|||
|
|||
|
|||
|
|
||
|
|||
|
|||
|
|
||
|
Наследуемые методы |
||
|
|
||
|
Унаследованные методы |
||
|
|
||
|
Унаследованные методы |
||
|
|||
|
|||
|
|||
|
-
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¶ ABC для классов, которые предоставляют методы
__iter__()иnext(). См. также определение iterator.
-
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__()и__ge__(), тогда остальные операции будут автоматически им следовать.Миксин
Setпредоставляет метод_hash()для вычисления хеша множества; однако__hash__()не определён, так как не все множества хешируемы или неизменяемы. Чтобы добавить хешируемость множества с помощью миксинов, унаследуйте от обоихSet()иHashable(), а затем определите__hash__ = Set._hash.
См. также
Рецепт OrderedSet для примера, построенного на
MutableSet.Для получения дополнительной информации об ABC см. модуль
abcи .