collections — Типы контейнерных данных
Исходный код: Lib/collections/__init__.py
Этот модуль реализует специализированные типы контейнерных данных, предоставляющие альтернативы встроенным контейнерам Python общего назначения, dict, list, set и tuple.
функция-фабрика для создания подклассов кортежей с именованными полями | |
подобный списку контейнер с быстрыми добавлениями и удалениями с обоих концов | |
подобный словарю класс для создания единого представления нескольких словарей | |
подкласс словаря для подсчета хешируемых объектов | |
подкласс словаря, запоминающий порядок добавления элементов | |
подкласс словаря, вызывающий функцию-фабрику для обеспечения пропущенных значений | |
обертка вокруг объектов словаря для более простого создания подклассов словарей | |
обертка вокруг объектов списков для более простого создания подклассов списков | |
обертка вокруг строковых объектов для более простого создания подклассов строк |
Объекты 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 в пакете Enthought CodeTools имеет параметры для поддержки записи в любое отображение в цепочке.
- Класс Context Django для шаблонов представляет собой неизменяемую цепочку отображений. Он также включает в себя push и pop контектсов, аналогичных методу
new_child()и свойствуparents. - Рецепт Nested Contexts имеет параметры для управления тем, применяются ли записи и другие изменения только к первому отображению или ко всем отображениям в цепочке.
- Очень упрощённая версия 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
Предлагается инструмент 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) возвращает true.
Добавлена в версии 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() # convert to a list of (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++ multisets tutorial с примерами.
- Для математических операций над множествами с повторениями и их случаев использования см. Knuth, Donald. The Art of Computer Programming Volume II, Section 4.6.3, Exercise 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()) данными из iterable. Если iterable не указан, новый deque пустой.Deque — это обобщение стеков и очередей (название произносится как «дек» и сокращение от «двухсторонней очереди»). Deque поддерживают безопасные для потоков, эффективные с точки зрения памяти добавления и извлечения элементов с любой стороны deque с примерно одинаковым временем выполнения O(1) в любом направлении.
Хотя объекты
listподдерживают аналогичные операции, они оптимизированы для быстрых операций с фиксированной длиной и несут затраты O(n) на перемещение памяти при операцияхpop(0)иinsert(0, v), которые изменяют как размер, так и позицию базового представления данных.Если maxlen не указан или равен
None, деки могут расти до произвольной длины. В противном случае длина deque ограничена указанной максимальной длиной. После того как deque с ограниченной длиной заполнится, при добавлении новых элементов соответствующее количество элементов удаляется с противоположного конца. Deque с ограниченной длиной предоставляют функциональность, аналогичную фильтруtailв Unix. Они также полезны для отслеживания транзакций и других наборов данных, где интерес представляет только последняя активность.Объекты Deque поддерживают следующие методы:
-
append(x) -
Добавить x в правую сторону deque.
-
appendleft(x) -
Добавить x в левую сторону deque.
-
clear() -
Удалить все элементы из deque, оставив его длиной 0.
-
copy() -
Создать поверхностную копию deque.
Новая в версии 3.5.
-
count(x) -
Подсчитать количество элементов deque, равных x.
Новая в версии 3.2.
-
extend(iterable) -
Расширить правую сторону deque, добавив элементы из аргумента iterable.
-
extendleft(iterable) -
Расширить левую сторону deque, добавив элементы из iterable. Обратите внимание, что последовательность добавлений слева приводит к изменению порядка элементов в аргументе iterable.
-
index(x[, start[, stop]]) -
Возвращает позицию x в deque (в или после индекса start и перед индексом stop). Возвращает первое совпадение или вызывает
ValueError, если не найдено.Новая в версии 3.5.
-
insert(i, x) -
Вставить x в deque в позицию i.
Если вставка приведет к тому, что ограниченный deque вырастет за пределы maxlen, генерируется
IndexError.Новая в версии 3.5.
-
pop() -
Удалить и вернуть элемент с правой стороны deque. Если элементов нет, генерируется
IndexError.
-
popleft() -
Удалить и вернуть элемент с левой стороны deque. Если элементов нет, генерируется
IndexError.
-
remove(value) -
Удалить первое вхождение value. Если не найдено, генерируется
ValueError.
-
reverse() -
Инвертировать элементы deque на месте и вернуть
None.Новая в версии 3.2.
-
rotate(n=1) -
Повернуть deque на n шагов вправо. Если n отрицательное, повернуть влево.
Когда deque не пустой, поворот на один шаг вправо эквивалентен
d.appendleft(d.pop()), а поворот на один шаг влево эквивалентенd.append(d.popleft()).
-
maxlen -
Максимальный размер deque или
Noneпри неограниченном размере.Новая в версии 3.1.
-
Помимо вышеперечисленного, deques поддерживают итерацию, сериализацию, len(d), reversed(d), copy.copy(d), copy.deepcopy(d), проверку на вхождение с помощью оператора in и ссылки на индексы, такие как d[0] для доступа к первому элементу. Индексированный доступ — O(1) на обоих концах, но замедляется до O(n) посредине. Для быстрого произвольного доступа используйте списки.
Начиная с версии 3.5, deques поддерживают __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 Рецепты
Этот раздел демонстрирует различные подходы к работе с deques.
Deques с ограниченной длиной предоставляют функциональность, аналогичную фильтру tail в Unix:
def tail(filename, n=10):
'Return the last n lines of a file'
with open(filename) as f:
return deque(f, n)
Другой способ использования deque — поддерживать последовательность недавно добавленных элементов, добавляя вправо и удаляя влево:
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] полагается на метод 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.
Объекты 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: Добавлены операторы merge (
|) и update (|=) , описанные в 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(), чтобы предоставить значение по умолчанию 0. Операция инкремента затем наращивает счёт для каждой буквы.
Функция 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__атрибут именованного кортежа устанавливается в это значение.Экземпляры именованных кортежей не имеют словарей на экземпляр, поэтому они легкие и не требуют больше памяти, чем обычные кортежи.
Для поддержки сериализации подкласс именованного кортежа должен быть присвоен переменной, соответствующей 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())
-
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) -
Перемещает существующий ключ в начало или конец упорядоченного словаря. Элемент перемещается в конец, если last равно True (по умолчанию), или в начало, если last равно False. Возбуждает
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 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(0)
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(0)
else:
self.requests.pop(args, None)
self.cache[args] = result
if len(self.cache) > self.maxsize:
self.cache.popitem(0)
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–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.11/library/collections.html