collections — Типы данных-контейнеры
Исходный код: Lib/collections/__init__.py
Этот модуль реализует специализированные типы данных-контейнеры, предоставляющие альтернативы встроенным контейнерам Python общего назначения: dict, list, set и tuple.
функция-фабрика для создания подклассов кортежей с именованными полями | |
контейнер, похожий на список, с быстрым добавлением и удалением элементов с обоих концов | |
класс, похожий на словарь, для создания единого представления нескольких отображений | |
подкласс dict для подсчёта хешируемых объектов | |
подкласс dict, сохраняющий порядок добавления записей | |
подкласс dict, вызывающий функцию-фабрику для получения отсутствующих значений | |
обёртка вокруг объектов словарей для упрощения создания подклассов dict | |
обёртка вокруг объектов списков для упрощения создания подклассов list | |
обёртка вокруг объектов строк для упрощения создания подклассов строк |
Объекты ChainMap
Добавлено в версии 3.3.
Класс ChainMap позволяет быстро связывать несколько отображений, чтобы обращаться с ними как с единым целым. Это часто намного быстрее, чем создавать новый словарь и выполнять несколько вызовов update().
Класс можно использовать для моделирования вложенных областей видимости; он полезен при создании шаблонов.
-
class collections.ChainMap(*maps) -
ChainMapобъединяет несколько словарей или других отображений, создавая единое представление, которое можно обновлять. Если аргумент maps не указан, создаётся один пустой словарь, чтобы новая цепочка всегда содержала хотя бы одно отображение.Исходные отображения хранятся в списке. Этот список является открытым и доступен для чтения и изменения через атрибут maps. Других внутренних данных нет.
Поиск последовательно выполняется в исходных отображениях, пока не будет найден ключ. В отличие от этого, операции записи, обновления и удаления затрагивают только первое отображение.
ChainMapвключает исходные отображения по ссылке. Поэтому, если одно из исходных отображений обновляется, эти изменения отражаются вChainMap.Поддерживаются все обычные методы словарей. Кроме того, имеются атрибут maps, метод для создания новых подконтекстов и свойство для доступа ко всем отображениям, кроме первого:
-
maps -
Список отображений, доступный пользователю для изменения. Список упорядочен от первого просматриваемого отображения к последнему. Это единственное хранимое состояние; его можно изменить, чтобы поменять набор просматриваемых отображений. Список всегда должен содержать хотя бы одно отображение.
-
new_child(m=None, **kwargs) -
Возвращает новый объект
ChainMap, содержащий новое отображение, за которым следуют все отображения текущего экземпляра. Если указанm, он становится новым отображением в начале списка; если он не указан, используется пустой словарь, поэтому вызовd.new_child()эквивалентен:ChainMap({}, *d.maps). Если указаны именованные аргументы, они обновляют переданное отображение или новый пустой словарь. Этот метод используется для создания подконтекстов, которые можно обновлять, не изменяя значения в родительских отображениях.Изменено в версии 3.4: Добавлен необязательный параметр
m.Изменено в версии 3.10: Добавлена поддержка именованных аргументов.
-
parents -
Свойство, возвращающее новый объект
ChainMap, содержащий все отображения текущего экземпляра, кроме первого. Это полезно, чтобы пропустить первое отображение при поиске. Сценарии использования похожи на применение ключевого словаnonlocalво вложенных областях видимости. Эти сценарии также аналогичны применению встроенной функцииsuper(). Ссылка наd.parentsэквивалентна:ChainMap(*d.maps[1:]).
Обратите внимание: порядок итерации объекта
ChainMapопределяется обходом отображений от последнего к первому:>>> baseline = {'music': 'bach', 'art': 'rembrandt'} >>> adjustments = {'art': 'van gogh', 'opera': 'carmen'} >>> list(ChainMap(adjustments, baseline)) ['music', 'art', 'opera']Это даёт такой же порядок, как серия вызовов
dict.update(), начинающаяся с последнего отображения:>>> combined = baseline.copy() >>> combined.update(adjustments) >>> list(combined) ['music', 'art', 'opera']
Изменено в версии 3.9: Добавлена поддержка операторов
|и|=, описанных в PEP 584. -
См. также
- Класс MultiContext из пакета CodeTools от Enthought предоставляет возможности записи в любое отображение цепочки.
- Класс Context в Django для работы с шаблонами представляет собой цепочку отображений, доступную только для чтения. Он также поддерживает добавление и извлечение контекстов, подобно методу
new_child()и свойствуparents. - В рецепте Вложенные контексты предусмотрены настройки, определяющие, применяются ли записи и другие изменения только к первому отображению или к любому отображению цепочки.
- Сильно упрощённая версия ChainMap, доступная только для чтения.
Примеры и рецепты для ChainMap
В этом разделе показаны различные способы работы со связанными отображениями.
Пример моделирования внутренней цепочки поиска Python:
import builtins pylookup = ChainMap(locals(), globals(), vars(builtins))
Пример, в котором пользовательские аргументы командной строки имеют приоритет над переменными окружения, а переменные окружения, в свою очередь, — над значениями по умолчанию:
import os, argparse
defaults = {'color': 'red', 'user': 'guest'}
parser = argparse.ArgumentParser()
parser.add_argument('-u', '--user')
parser.add_argument('-c', '--color')
namespace = parser.parse_args()
command_line_args = {k: v for k, v in vars(namespace).items() if v is not None}
combined = ChainMap(command_line_args, os.environ, defaults)
print(combined['color'])
print(combined['user'])
Примеры использования класса ChainMap для моделирования вложенных контекстов:
c = ChainMap() # Create root context d = c.new_child() # Create nested child context e = c.new_child() # Child of c, independent from d e.maps[0] # Current context dictionary -- like Python's locals() e.maps[-1] # Root context -- like Python's globals() e.parents # Enclosing context chain -- like Python's nonlocals d['x'] = 1 # Set value in current context d['x'] # Get first key in the chain of contexts del d['x'] # Delete from current context list(d) # All nested values k in d # Check all nested values len(d) # Number of nested values d.items() # All nested items dict(d) # Flatten into a regular dictionary
Класс ChainMap выполняет обновления (записи и удаления) только в первом отображении цепочки, тогда как поиск выполняется по всей цепочке. Однако, если требуется выполнять записи и удаления во вложенных отображениях, можно легко создать подкласс, который обновляет ключи, найденные глубже в цепочке:
class DeepChainMap(ChainMap):
'Variant of ChainMap that allows direct updates to inner scopes'
def __setitem__(self, key, value):
for mapping in self.maps:
if key in mapping:
mapping[key] = value
return
self.maps[0][key] = value
def __delitem__(self, key):
for mapping in self.maps:
if key in mapping:
del mapping[key]
return
raise KeyError(key)
>>> d = DeepChainMap({'zebra': 'black'}, {'elephant': 'blue'}, {'lion': 'yellow'})
>>> d['lion'] = 'orange' # update an existing key two levels down
>>> d['snake'] = 'red' # new keys get added to the topmost dict
>>> del d['elephant'] # remove an existing key one level down
>>> d # display result
DeepChainMap({'zebra': 'black', 'snake': 'red'}, {}, {'lion': 'orange'})
Объекты Counter
Для удобного и быстрого подсчёта предусмотрен инструмент-счётчик. Например:
>>> # Tally occurrences of words in a list
>>> cnt = Counter()
>>> for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']:
... cnt[word] += 1
...
>>> cnt
Counter({'blue': 3, 'red': 2, 'green': 1})
>>> # Find the ten most common words in Hamlet
>>> 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() # a new, empty counter >>> c = Counter('gallahad') # a new counter from an iterable >>> c = Counter({'red': 4, 'blue': 2}) # a new counter from a mapping >>> c = Counter(cats=4, dogs=8) # a new counter from keyword argsОбъекты Counter предоставляют интерфейс словаря, за исключением того, что для отсутствующих элементов они возвращают нулевое количество, а не вызывают исключение
KeyError:>>> c = Counter(['eggs', 'ham']) >>> c['bacon'] # count of a missing element is zero 0
Присваивание элементу нулевого количества не удаляет его из счётчика. Чтобы полностью удалить его, используйте
del:>>> c['sausage'] = 0 # counter entry with a zero count >>> del c['sausage'] # del actually removes the entry
Добавлено в версии 3.1.
Изменено в версии 3.7: Как подкласс
dict, классCounterунаследовал возможность запоминать порядок вставки. Математические операции над объектами Counter также сохраняют порядок. Результаты упорядочиваются по моменту первого появления элемента в левом операнде, а затем — по порядку появления в правом операнде.Объекты Counter поддерживают дополнительные методы помимо тех, которые доступны всем словарям:
-
elements() -
Возвращает итератор по элементам, повторяя каждый столько раз, сколько указано в его количестве. Элементы возвращаются в порядке первого появления. Если количество элемента меньше единицы,
elements()пропускает его.>>> c = Counter(a=4, b=2, c=0, d=-2) >>> sorted(c.elements()) ['a', 'a', 'a', 'a', 'b', 'b']
-
most_common([n]) -
Возвращает список из n наиболее часто встречающихся элементов и их количеств, от самых частых к наименее частым. Если n не указано или равно
None,most_common()возвращает все элементы счётчика. Элементы с одинаковым количеством располагаются в порядке первого появления:>>> Counter('abracadabra').most_common(3) [('a', 5), ('b', 2), ('r', 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})Добавлено в версии 3.2.
-
total() -
Вычисляет сумму количеств.
>>> c = Counter(a=10, b=5, c=0) >>> c.total() 15
Добавлено в версии 3.10.
Для объектов
Counterдоступны обычные методы словаря, за исключением двух методов, которые работают со счётчиками иначе.-
fromkeys(iterable) -
Этот метод класса не реализован для объектов
Counter.
-
update([iterable-or-mapping]) -
Подсчитывает элементы из итерируемого объекта или добавляет значения из другого отображения (или счётчика). Подобно
dict.update(), но количества складываются, а не заменяются. Кроме того, ожидается, что итерируемый объект — это последовательность элементов, а не последовательность пар(key, value).
-
Счётчики поддерживают расширенные операции сравнения на равенство, включение и включение в качестве надмножества: ==, !=, <, <=, >, >=. Во всех этих проверках отсутствующие элементы считаются имеющими нулевое количество, поэтому Counter(a=1) == Counter(a=1, b=0) возвращает истинное значение.
Изменено в версии 3.10: Добавлены расширенные операции сравнения.
Изменено в версии 3.10: При проверке на равенство отсутствующие элементы считаются имеющими нулевое количество. Ранее Counter(a=3) и Counter(a=3, b=0) считались различными.
Распространённые шаблоны работы с объектами Counter:
c.total() # total of all counts c.clear() # reset all counts list(c) # list unique elements set(c) # convert to a set dict(c) # convert to a regular dictionary c.items() # access the (elem, cnt) pairs Counter(dict(list_of_pairs)) # convert from a list of (elem, cnt) pairs c.most_common()[:-n-1:-1] # n least common elements +c # remove zero and negative counts
Для объединения объектов Counter и получения мультимножеств (счётчиков, в которых количества больше нуля) предусмотрено несколько математических операций. Сложение и вычитание объединяют счётчики, складывая или вычитая количества соответствующих элементов. Пересечение и объединение возвращают минимум и максимум соответствующих количеств. Равенство и включение сравнивают соответствующие количества. Каждая операция допускает входные данные с отрицательными количествами, но в результат не включаются элементы с нулевым или отрицательным количеством.
>>> c = Counter(a=3, b=1)
>>> d = Counter(a=1, b=2)
>>> c + d # add two counters together: c[x] + d[x]
Counter({'a': 4, 'b': 3})
>>> c - d # subtract (keeping only positive counts)
Counter({'a': 2})
>>> c & d # intersection: min(c[x], d[x])
Counter({'a': 1, 'b': 1})
>>> c | d # union: max(c[x], d[x])
Counter({'a': 3, 'b': 2})
>>> c == d # equality: c[x] == d[x]
False
>>> c <= d # inclusion: c[x] <= d[x]
False
Унарные сложение и вычитание — это сокращённые формы записи сложения с пустым счётчиком или вычитания из пустого счётчика.
>>> c = Counter(a=2, b=-4)
>>> +c
Counter({'a': 2})
>>> -c
Counter({'b': 4})
Добавлено в версии 3.3: Добавлена поддержка унарного плюса, унарного минуса и операций над мультимножествами с присваиванием.
Примечание
Счётчики предназначены прежде всего для работы с положительными целыми числами, представляющими текущие количества. Однако были приняты меры, чтобы без необходимости не исключать сценарии использования с другими типами или отрицательными значениями. Для поддержки таких сценариев в этом разделе описаны минимальные ограничения на диапазон и типы данных.
- Сам класс
Counterявляется подклассом словаря, не накладывающим ограничений на ключи и значения. Предполагается, что значения — это числа, представляющие количества, но в поле значения можно хранить что угодно. - Методу
most_common()требуется лишь, чтобы значения можно было упорядочивать. - Для операций с присваиванием, таких как
c[key] += 1, достаточно, чтобы тип значений поддерживал сложение и вычитание. Поэтому подойдут дроби, числа с плавающей точкой и десятичные числа; поддерживаются также отрицательные значения. То же относится к методамupdate()иsubtract(), допускающим отрицательные и нулевые значения как во входных данных, так и в результатах. - Методы для работы с мультимножествами предназначены только для сценариев с положительными значениями. Входные значения могут быть отрицательными или нулевыми, но создаются только результаты с положительными значениями. Ограничений на типы нет, однако тип значения должен поддерживать сложение, вычитание и сравнение.
- Методу
elements()нужны целочисленные количества. Он игнорирует нулевые и отрицательные значения.
См. также
- Класс 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
Объекты deque
-
class collections.deque([iterable[, maxlen]]) -
Возвращает новый объект deque, инициализированный слева направо (с использованием
append()) данными из итерируемого объекта. Если итерируемый объект не указан, новый deque будет пустым.Деки представляют собой обобщение стеков и очередей (название произносится как «дек» и является сокращением от «двусторонняя очередь»). Деки поддерживают потокобезопасное и эффективное по памяти добавление и удаление элементов с любого конца с приблизительно одинаковой производительностью O(1) в обоих направлениях.
Хотя объекты
listподдерживают похожие операции, они оптимизированы для быстрых операций с фиксированной длиной. При операцияхpop(0)иinsert(0, v), изменяющих размер и положение базового представления данных, они требуют перемещения памяти стоимостью O(n).Если maxlen не указан или равен
None, длина деков может быть произвольной. В противном случае длина дека ограничена указанным максимальным значением. Когда дек с ограниченной длиной заполнен, при добавлении новых элементов соответствующее число элементов удаляется с противоположного конца. Деки с ограниченной длиной обладают функциями, похожими на фильтрtailв Unix. Они также полезны для отслеживания транзакций и других наборов данных, в которых важны только самые последние события.Объекты deque поддерживают следующие методы:
-
append(x) -
Добавляет x в правую часть дека.
-
appendleft(x) -
Добавляет x в левую часть дека.
-
clear() -
Удаляет все элементы из дека, устанавливая его длину равной 0.
-
copy() -
Создаёт поверхностную копию дека.
Добавлено в версии 3.5.
-
count(x) -
Подсчитывает количество элементов дека, равных x.
Добавлено в версии 3.2.
-
extend(iterable) -
Расширяет правую часть дека, добавляя элементы из аргумента-итерируемого объекта.
-
extendleft(iterable) -
Расширяет левую часть дека, добавляя элементы из итерируемого объекта. Обратите внимание: последовательное добавление элементов слева приводит к изменению их порядка на противоположный.
-
index(x[, start[, stop]]) -
Возвращает позицию x в деке (начиная с индекса start и до индекса stop, не включая его). Возвращает первое совпадение или вызывает исключение
ValueError, если совпадение не найдено.Добавлено в версии 3.5.
-
insert(i, x) -
Вставляет x в дек на позицию i.
Если из-за вставки длина дека превысит maxlen, будет вызвано исключение
IndexError.Добавлено в версии 3.5.
-
pop() -
Удаляет и возвращает элемент из правой части дека. Если элементов нет, вызывает исключение
IndexError.
-
popleft() -
Удаляет и возвращает элемент из левой части дека. Если элементов нет, вызывает исключение
IndexError.
-
remove(value) -
Удаляет первое вхождение value. Если значение не найдено, вызывает исключение
ValueError.
-
reverse() -
Разворачивает элементы дека на месте, а затем возвращает
None.Добавлено в версии 3.2.
-
rotate(n=1) -
Поворачивает дек на n шагов вправо. Если n отрицательно, поворачивает его влево.
Если дек не пуст, поворот на один шаг вправо эквивалентен
d.appendleft(d.pop()), а поворот на один шаг влево эквивалентенd.append(d.popleft()).
Объекты deque также предоставляют один атрибут, доступный только для чтения:
-
maxlen -
Максимальный размер дека или
None, если длина не ограничена.Добавлено в версии 3.1.
-
Помимо перечисленного выше, деки поддерживают итерацию, сериализацию с помощью pickle, len(d), reversed(d), copy.copy(d), copy.deepcopy(d), проверку принадлежности с помощью оператора in и обращение по индексу, например d[0], для доступа к первому элементу. Доступ по индексу имеет сложность O(1) на обоих концах, но в середине замедляется до O(n). Для быстрого произвольного доступа используйте списки.
Начиная с версии 3.5 деки поддерживают __add__(), __mul__() и __imul__().
Пример:
>>> from collections import deque
>>> d = deque('ghi') # make a new deque with three items
>>> for elem in d: # iterate over the deque's elements
... print(elem.upper())
G
H
I
>>> d.append('j') # add a new entry to the right side
>>> d.appendleft('f') # add a new entry to the left side
>>> d # show the representation of the deque
deque(['f', 'g', 'h', 'i', 'j'])
>>> d.pop() # return and remove the rightmost item
'j'
>>> d.popleft() # return and remove the leftmost item
'f'
>>> list(d) # list the contents of the deque
['g', 'h', 'i']
>>> d[0] # peek at leftmost item
'g'
>>> d[-1] # peek at rightmost item
'i'
>>> list(reversed(d)) # list the contents of a deque in reverse
['i', 'h', 'g']
>>> 'h' in d # search the deque
True
>>> d.extend('jkl') # add multiple elements at once
>>> d
deque(['g', 'h', 'i', 'j', 'k', 'l'])
>>> d.rotate(1) # right rotation
>>> d
deque(['l', 'g', 'h', 'i', 'j', 'k'])
>>> d.rotate(-1) # left rotation
>>> d
deque(['g', 'h', 'i', 'j', 'k', 'l'])
>>> deque(reversed(d)) # make a new deque in reverse order
deque(['l', 'k', 'j', 'i', 'h', 'g'])
>>> d.clear() # empty the deque
>>> d.pop() # cannot pop from an empty 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() reverses the input order
>>> d
deque(['c', 'b', 'a'])
Рецепты для deque
В этом разделе показаны различные способы работы с деками.
Деки с ограниченной длиной обладают функциями, похожими на фильтр tail в Unix:
def tail(filename, n=10):
'Return the last n lines of a file'
with open(filename) as f:
return deque(f, n)
Другой способ использовать деки — хранить последовательность недавно добавленных элементов, добавляя их справа и извлекая слева:
def moving_average(iterable, n=3):
# moving_average([40, 30, 50, 46, 39, 44]) --> 40.0 42.0 45.0 43.0
# https://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
Планировщик циклического перебора можно реализовать, сохраняя входные итераторы в объекте deque. Значения выдаются из активного итератора, находящегося на позиции ноль. Если итератор исчерпан, его можно удалить с помощью popleft(); в противном случае его можно переместить в конец с помощью метода rotate():
def roundrobin(*iterables):
"roundrobin('ABC', 'D', 'EF') --> A D E B F C"
iterators = deque(map(iter, iterables))
while iterators:
try:
while True:
yield next(iterators[0])
iterators.rotate(-1)
except StopIteration:
# Remove an exhausted iterator.
iterators.popleft()
Метод rotate() позволяет реализовать срезы и удаление элементов deque. Например, реализация del d[n] на чистом Python использует метод rotate(), чтобы переместить элементы в нужное положение перед извлечением:
def delete_nth(d, n):
d.rotate(-n)
d.popleft()
d.rotate(n)
Для реализации срезов deque примените аналогичный подход: вызовите rotate(), чтобы переместить нужный элемент в левую часть дека. Удалите старые записи с помощью popleft(), добавьте новые записи с помощью extend(), а затем отмените поворот. С небольшими изменениями этого подхода легко реализовать манипуляции со стеком в стиле Forth, такие как dup, drop, swap, over, pick, rot и roll.
Объекты defaultdict
-
class collections.defaultdict(default_factory=None, /[, ...]) -
Возвращает новый объект, похожий на словарь.
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__().Обратите внимание, что
__missing__()вызывается только при выполнении операции__getitem__(). Это означает, чтоget(), как и для обычных словарей, вернётNoneпо умолчанию, а не будет использоватьdefault_factory.
Объекты
defaultdictподдерживают следующую переменную экземпляра:-
default_factory -
Этот атрибут используется методом
__missing__(); он инициализируется первым аргументом конструктора, если он указан, либо значениемNone, если аргумент не указан.
Изменено в версии 3.9: Добавлены операторы слияния (
|) и обновления (|=), описанные в PEP 584. -
defaultdict Примеры
Если использовать 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)
...
>>> sorted(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)
...
>>> sorted(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
...
>>> sorted(d.items())
[('i', 4), ('m', 1), ('p', 2), ('s', 4)]
При первом появлении буквы её ещё нет в отображении, поэтому функция 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)
...
>>> sorted(d.items())
[('blue', {2, 4}), ('red', {1, 3})]
Фабричная функция namedtuple() для кортежей с именованными полями
Именованные кортежи придают смысл каждой позиции в кортеже и позволяют писать более читаемый и самодокументируемый код. Их можно использовать везде, где используются обычные кортежи; кроме того, они позволяют обращаться к полям по имени, а не по индексу позиции.
-
collections.namedtuple(typename, field_names, *, rename=False, defaults=None, module=None) -
Возвращает новый подкласс кортежа с именем typename. Этот подкласс используется для создания объектов, похожих на кортежи: к их полям можно обращаться по атрибуту, а также индексировать их и выполнять итерацию. Экземпляры подкласса также имеют полезную строку документации (с указанием typename и field_names) и полезный метод
__repr__(), который выводит содержимое кортежа в форматеname=value.field_names — это последовательность строк, например
['x', 'y']. Также field_names может быть одной строкой, в которой имена полей разделены пробельными символами и/или запятыми, например'x y'или'x, y'.В качестве имени поля можно использовать любой допустимый идентификатор Python, кроме имён, начинающихся с подчёркивания. Допустимые идентификаторы состоят из букв, цифр и подчёркиваний, но не начинаются с цифры или подчёркивания и не могут быть
keyword, например class, for, return, global, pass или raise.Если значение rename равно true, недопустимые имена полей автоматически заменяются позиционными именами. Например,
['abc', 'def', 'ghi', 'abc']преобразуется в['abc', '_1', 'ghi', '_3'], что устраняет ключевое словоdefи повторяющееся имя поляabc.defaults может иметь значение
Noneили быть итерируемым объектом со значениями по умолчанию. Поскольку поля со значением по умолчанию должны следовать за полями без значения по умолчанию, значения из defaults применяются к параметрам справа. Например, если имена полей —['x', 'y', 'z'], а значения по умолчанию —(1, 2), тоxбудет обязательным аргументом, дляyзначением по умолчанию будет1, а дляz—2.Если задан module, атрибут
__module__именованного кортежа получает это значение.У экземпляров именованных кортежей нет собственных словарей экземпляров, поэтому они обладают малым весом и требуют не больше памяти, чем обычные кортежи.
Для поддержки сериализации с помощью pickle класс именованного кортежа следует присвоить переменной, имя которой совпадает с typename.
Изменено в версии 3.1: Добавлена поддержка rename.
Изменено в версии 3.6: Параметры verbose и rename стали именованными аргументами.
Изменено в версии 3.6: Добавлен параметр module.
Изменено в версии 3.7: Удалены параметр verbose и атрибут
_source.Изменено в версии 3.7: Добавлены параметр defaults и атрибут
_field_defaults.
>>> # Basic example
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(11, y=22) # instantiate with positional or keyword arguments
>>> p[0] + p[1] # indexable like the plain tuple (11, 22)
33
>>> x, y = p # unpack like a regular tuple
>>> x, y
(11, 22)
>>> p.x + p.y # fields also accessible by name
33
>>> p # readable __repr__ with a name=value style
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() -
Возвращает новый объект
dict, сопоставляющий имена полей с соответствующими значениями:>>> p = Point(x=11, y=22) >>> p._asdict() {'x': 11, 'y': 22}Изменено в версии 3.1: Возвращает объект
OrderedDictвместо обычногоdict.Изменено в версии 3.8: Возвращает обычный
dictвместоOrderedDict. Начиная с Python 3.7, для обычных словарей гарантируется сохранение порядка. Если требуются дополнительные возможностиOrderedDict, рекомендуется преобразовать результат в нужный тип:OrderedDict(nt._asdict()).
-
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())
Именованные кортежи также поддерживаются обобщённой функцией
copy.replace().Изменено в версии 3.13: Для недопустимых именованных аргументов вызывается исключение
TypeErrorвместоValueError.
-
somenamedtuple._fields -
Кортеж строк со списком имён полей. Полезен для интроспекции и создания новых типов именованных кортежей на основе существующих.
>>> p._fields # view the field names ('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)
-
somenamedtuple._field_defaults -
Словарь, сопоставляющий имена полей значениям по умолчанию.
>>> Account = namedtuple('Account', ['type', 'balance'], defaults=[0]) >>> Account._field_defaults {'balance': 0} >>> Account('premium') Account(type='premium', balance=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',))
Строки документации можно настроить, напрямую присвоив значения полям __doc__:
>>> Book = namedtuple('Book', ['id', 'title', 'authors'])
>>> Book.__doc__ += ': Hardcover book in active collection'
>>> Book.id.__doc__ = '13-digit ISBN'
>>> Book.title.__doc__ = 'Title of first printing'
>>> Book.authors.__doc__ = 'List of authors sorted by last name'
Изменено в версии 3.5: Строки документации свойств стали доступны для записи.
См. также
-
См.
typing.NamedTuple, чтобы узнать, как добавлять аннотации типов к именованным кортежам. В нём также предлагается элегантная нотация с использованием ключевого словаclass:class Component(NamedTuple): part_number: int weight: float description: Optional[str] = None - См.
types.SimpleNamespace()— изменяемое пространство имён на основе словаря, а не кортежа. - Модуль
dataclassesпредоставляет декоратор и функции для автоматического добавления сгенерированных специальных методов в классы, определённые пользователем.
Объекты OrderedDict
Упорядоченные словари похожи на обычные словари, но обладают дополнительными возможностями для операций изменения порядка. Их значение уменьшилось после того, как встроенный класс dict научился сохранять порядок вставки (это поведение стало гарантированным в Python 3.7).
Тем не менее сохраняются некоторые отличия от dict:
- Обычный
dictбыл разработан для эффективного выполнения операций отображения. Сохранение порядка вставки было второстепенной задачей. OrderedDictбыл разработан для эффективного выполнения операций изменения порядка. Экономия памяти, скорость итерации и производительность операций обновления были второстепенными задачами.- Алгоритм
OrderedDictлучше справляется с частыми операциями изменения порядка, чемdict. Как показано в рецептах ниже, это делает его подходящим для реализации различных вариантов LRU-кэшей. -
Операция проверки равенства для
OrderedDictучитывает совпадение порядка.Обычный
dictможет имитировать проверку равенства с учётом порядка с помощьюp == q and all(k1 == k2 for k1, k2 in zip(p, q)). -
Метод
popitem()классаOrderedDictимеет другую сигнатуру. Он принимает необязательный аргумент, указывающий, какой элемент извлечь.Обычный
dictможет имитироватьod.popitem(last=True)класса OrderedDict с помощьюd.popitem(), который гарантированно извлекает крайний правый (последний) элемент.Обычный
dictможет имитироватьod.popitem(last=False)класса OrderedDict с помощью(k := next(iter(d)), d.pop(k)), который возвращает и удаляет крайний левый (первый) элемент, если он существует. -
В классе
OrderedDictесть методmove_to_end(), позволяющий эффективно перемещать элемент в один из концов.Обычный
dictможет имитироватьod.move_to_end(k, last=True)класса OrderedDict с помощьюd[k] = d.pop(k), который переместит ключ и связанное с ним значение в крайнюю правую (последнюю) позицию.У обычного
dictнет эффективного аналогаod.move_to_end(k, last=False)класса OrderedDict, который перемещает ключ и связанное с ним значение в крайнюю левую (первую) позицию. - До Python 3.8 у
dictне было метода__reversed__().
-
class collections.OrderedDict([items]) -
Возвращает экземпляр подкласса
dictсо специализированными методами для изменения порядка элементов словаря.Добавлено в версии 3.1.
-
popitem(last=True) -
Метод
popitem()упорядоченных словарей возвращает и удаляет пару (ключ, значение). Пары возвращаются в порядке LIFO, если значение last равно true, или в порядке FIFO, если оно равно false.
-
move_to_end(key, last=True) -
Перемещает существующий key в один из концов упорядоченного словаря. Если значение last равно true (по умолчанию), элемент перемещается в правый конец; если значение last равно false — в начало. Если key не существует, возникает исключение
KeyError:>>> d = OrderedDict.fromkeys('abcde') >>> d.move_to_end('b') >>> ''.join(d) 'acdeb' >>> d.move_to_end('b', last=False) >>> ''.join(d) 'bacde'Добавлено в версии 3.2.
-
Помимо обычных методов отображения, упорядоченные словари также поддерживают обратную итерацию с помощью reversed().
Проверка равенства объектов OrderedDict учитывает порядок и примерно эквивалентна list(od1.items())==list(od2.items()).
Проверка равенства объектов OrderedDict и других объектов Mapping не учитывает порядок, как и для обычных словарей. Благодаря этому объекты OrderedDict можно использовать везде, где применяются обычные словари.
Изменено в версии 3.5: Представления элементов, ключей и значений объекта OrderedDict теперь поддерживают обратную итерацию с помощью reversed().
Изменено в версии 3.6: После принятия PEP 468 сохраняется порядок именованных аргументов, переданных конструктору OrderedDict и его методу update().
Изменено в версии 3.9: Добавлены операторы слияния (|) и обновления (|=), описанные в PEP 584.
OrderedDict Примеры и рецепты
Несложно создать вариант упорядоченного словаря, который запоминает порядок, в котором ключи были вставлены последний раз. Если новая запись заменяет существующую, исходная позиция вставки меняется, и запись перемещается в конец:
class LastUpdatedOrderedDict(OrderedDict):
'Store items in the order the keys were last added'
def __setitem__(self, key, value):
super().__setitem__(key, value)
self.move_to_end(key)
OrderedDict также пригодится для реализации вариантов functools.lru_cache():
from collections import OrderedDict
from time import time
class TimeBoundedLRU:
"LRU Cache that invalidates and refreshes old entries."
def __init__(self, func, maxsize=128, maxage=30):
self.cache = OrderedDict() # { args : (timestamp, result)}
self.func = func
self.maxsize = maxsize
self.maxage = maxage
def __call__(self, *args):
if args in self.cache:
self.cache.move_to_end(args)
timestamp, result = self.cache[args]
if time() - timestamp <= self.maxage:
return result
result = self.func(*args)
self.cache[args] = time(), result
if len(self.cache) > self.maxsize:
self.cache.popitem(last=False)
return result
class MultiHitLRUCache:
""" LRU cache that defers caching a result until
it has been requested multiple times.
To avoid flushing the LRU cache with one-time requests,
we don't cache until a request has been made more than once.
"""
def __init__(self, func, maxsize=128, maxrequests=4096, cache_after=1):
self.requests = OrderedDict() # { uncached_key : request_count }
self.cache = OrderedDict() # { cached_key : function_result }
self.func = func
self.maxrequests = maxrequests # max number of uncached requests
self.maxsize = maxsize # max number of stored return values
self.cache_after = cache_after
def __call__(self, *args):
if args in self.cache:
self.cache.move_to_end(args)
return self.cache[args]
result = self.func(*args)
self.requests[args] = self.requests.get(args, 0) + 1
if self.requests[args] <= self.cache_after:
self.requests.move_to_end(args)
if len(self.requests) > self.maxrequests:
self.requests.popitem(last=False)
else:
self.requests.pop(args, None)
self.cache[args] = result
if len(self.cache) > self.maxsize:
self.cache.popitem(last=False)
return result
Объекты UserDict
Класс UserDict служит обёрткой для объектов-словарей. Необходимость в этом классе частично отпала благодаря возможности напрямую наследоваться от dict; однако с этим классом может быть удобнее работать, поскольку базовый словарь доступен как атрибут.
-
class collections.UserDict([initialdata]) -
Класс, имитирующий словарь. Содержимое экземпляра хранится в обычном словаре, доступном через атрибут
dataэкземпляровUserDict. Если указан initialdata, атрибутdataинициализируется его содержимым; обратите внимание, что ссылка на initialdata не сохраняется, поэтому этот объект можно использовать и для других целей.Помимо поддержки методов и операций отображений, экземпляры
UserDictпредоставляют следующий атрибут:-
data -
Обычный словарь, используемый для хранения содержимого класса
UserDict.
-
Объекты UserList
Этот класс служит обёрткой для объектов списка. Он представляет собой полезный базовый класс для собственных классов, подобных спискам, которые могут наследовать от него и переопределять существующие методы или добавлять новые. Таким образом можно добавлять спискам новые возможности.
Необходимость в этом классе отчасти отпала благодаря возможности напрямую создавать подклассы list; однако с этим классом может быть проще работать, поскольку базовый список доступен в виде атрибута.
-
class collections.UserList([list]) -
Класс, имитирующий список. Содержимое экземпляра хранится в обычном списке, доступном через атрибут
dataэкземпляровUserList. Изначально содержимое экземпляра задаётся копией list, по умолчанию — пустым списком[]. list может быть любым итерируемым объектом, например обычным списком Python или объектомUserList.Помимо поддержки методов и операций изменяемых последовательностей, экземпляры
UserListпредоставляют следующий атрибут:
Требования к подклассам: Предполагается, что подклассы UserList предоставляют конструктор, который можно вызвать без аргументов или с одним аргументом. Операции со списками, возвращающие новую последовательность, пытаются создать экземпляр фактического класса реализации. Для этого предполагается, что конструктор можно вызвать с одним параметром — объектом последовательности, используемым в качестве источника данных.
Если производный класс не должен соответствовать этому требованию, необходимо переопределить все специальные методы, поддерживаемые этим классом; сведения о методах, которые в этом случае нужно предоставить, см. в исходном коде.
Объекты UserString
Класс UserString служит обёрткой для строковых объектов. Необходимость в этом классе отчасти отпала благодаря возможности напрямую создавать подклассы str; однако с этим классом может быть проще работать, поскольку базовая строка доступна в виде атрибута.
-
class collections.UserString(seq) -
Класс, имитирующий строковый объект. Содержимое экземпляра хранится в обычном строковом объекте, доступном через атрибут
dataэкземпляровUserString. Изначально содержимое экземпляра задаётся копией seq. Аргумент seq может быть любым объектом, который можно преобразовать в строку с помощью встроенной функцииstr().Помимо поддержки методов и операций строк, экземпляры
UserStringпредоставляют следующий атрибут:-
data -
Обычный объект
str, используемый для хранения содержимого классаUserString.
Изменено в версии 3.5: Добавлены новые методы
__getnewargs__,__rmod__,casefold,format_map,isprintableиmaketrans. -
© 2001–2025 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/library/collections.html