collections
— Типы данных контейнеров¶
Исходный код: Lib/collections/__init__.py
Этот модуль реализует специализированные типы контейнерных данных, предоставляя альтернативы встроенным контейнерам общего назначения в Python, dict
, list
, set
, и tuple
.
фабричная функция для создания подклассов кортежей с именованными полями |
|
контейнер в виде списка с быстрым добавлением и всплывающим окном на обоих концах |
|
класс, подобный dict, для создания единого представления нескольких отображений |
|
подкласс dict для подсчета hashable объектов |
|
был добавлен подкласс dict, который запоминает записи заказа |
|
подкласс dict, вызывающий фабричную функцию для ввода пропущенных значений |
|
обертка вокруг объектов словаря для упрощения подкласса dict |
|
обертка вокруг объектов списка для упрощения подклассификации списков |
|
обертка вокруг строковых объектов для упрощения подклассификации строк |
ChainMap
объекты¶
Добавлено в версии 3.3.
Класс ChainMap
предназначен для быстрого связывания нескольких сопоставлений, чтобы их можно было рассматривать как единое целое. Зачастую это намного быстрее, чем создавать новый словарь и выполнять несколько вызовов update()
.
Этот класс можно использовать для моделирования вложенных областей и он полезен при создании шаблонов.
- class collections.ChainMap(*maps)¶
ChainMap
группирует несколько словарей или других отображений вместе, чтобы создать единое, доступное для обновления представление. Если не указано ни одного отображения, предоставляется один пустой словарь, чтобы новая цепочка всегда содержала по крайней мере одно отображение.Базовые сопоставления хранятся в виде списка. Этот список является общедоступным и может быть доступен или обновлен с помощью атрибута maps. Другого состояния нет.
При поиске выполняется последовательный поиск базовых отображений, пока не будет найден ключ. Напротив, операции записи, обновления и удаления выполняются только при первом отображении.
ChainMap
содержит базовые отображения по ссылке. Таким образом, если одно из базовых отображений будет обновлено, эти изменения будут отражены вChainMap
.Поддерживаются все обычные методы работы со словарем. Кроме того, есть атрибут maps, метод для создания новых вложенных текстов и свойство для доступа ко всем отображениям, кроме первого:
- maps¶
Список отображений, который может быть обновлен пользователем. Список упорядочен от первого к последнему. Это единственное сохраненное состояние, и его можно изменить, чтобы изменить, какие отображения ищутся. Список всегда должен содержать по крайней мере одно отображение.
- new_child(m=None, **kwargs)¶
Возвращает новый
ChainMap
, содержащий новую карту, за которой следуют все карты текущего экземпляра. Если указано значениеm
, оно становится новой картой в начале списка сопоставлений; если не указано, используется пустой dict, так что вызовd.new_child()
эквивалентен:ChainMap({}, *d.maps)
. Если указаны какие-либо аргументы ключевого слова, они обновляют переданную map или создают новый пустой dict. Этот метод используется для создания вложенных контекстов, которые могут быть обновлены без изменения значений в любом из родительских отображений.Изменено в версии 3.4: Был добавлен необязательный параметр
m
.Изменено в версии 3.10: Добавлена поддержка аргументов по ключевым словам.
- parents¶
Свойство, возвращающее новый
ChainMap
, содержащий все карты в текущем экземпляре, кроме первой. Это полезно для пропуска первой карты в поиске. Варианты использования аналогичны вариантам для ключевого словаnonlocal
, используемого в nested scopes. Варианты использования также аналогичны вариантам для встроенной функции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 class в Enthought CodeTools package есть опции для поддержки записи в любое отображение в цепочке.
Context class для создания шаблонов в Django - это цепочка сопоставлений, доступная только для чтения. В ней также есть функция нажатия и вывода контекстов, аналогичная методу
new_child()
и свойствуparents
.В Nested Contexts recipe есть опции для управления тем, применяются ли записи и другие мутации только к первому отображению или к любому отображению в цепочке.
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
подкласс для подсчета hashable объектов. Это коллекция, в которой элементы хранятся как ключи словаря, а их количество - как значения словаря. Значения Counts могут быть любыми целыми, включая нулевые или отрицательные значения. КлассCounter
аналогичен bags или multisets в других языках.Элементы подсчитываются из повторяемого или инициализируются из другого отображения (или счетчика):
>>> 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
Объекты счетчика имеют интерфейс словаря, за исключением того, что они возвращают нулевое количество пропущенных элементов вместо
KeyError
:>>> c = Counter(['eggs', 'ham']) >>> c['bacon'] # count of a missing element is zero 0
Установка значения count равным нулю не приводит к удалению элемента из счетчика. Используйте
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 также сохраняют порядок. Результаты упорядочиваются в соответствии с тем, когда элемент впервые встречается в левом операнде, а затем в порядке, в котором он встречается в правом операнде.Объекты-счетчики поддерживают дополнительные методы, помимо тех, которые доступны для всех словарей:
- 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
, за исключением двух, которые работают по-разному для счетчиков.- update([iterable-or-mapping])¶
Элементы подсчитываются с помощью повторяемого или добавляемого из другого сопоставления (или счетчика). Как
dict.update()
, но добавляет значения, а не заменяет их. Кроме того, ожидается, что iterable будет представлять собой последовательность элементов, а не последовательность пар(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
сам по себе является подклассом словаря без каких-либо ограничений на его ключи и значения. Предполагается, что значения будут числами, представляющими значения, но вы можете хранить что угодно в поле value.Метод
most_common()
требует только, чтобы значения были упорядочены.Для операций на месте, таких как
c[key] += 1
, тип значения должен поддерживать только сложение и вычитание. Таким образом, будут работать дроби, числа с плавающей точкой и десятичные дроби, а также поддерживаются отрицательные значения. То же самое верно и дляupdate()
иsubtract()
, которые допускают отрицательные и нулевые значения как для входов, так и для выходов.Методы multiset предназначены только для случаев использования с положительными значениями. Входные данные могут быть отрицательными или нулевыми, но создаются только выходные данные с положительными значениями. Ограничений по типу нет, но тип значения должен поддерживать сложение, вычитание и сравнение.
Метод
elements()
требует подсчета целых чисел. Он игнорирует нулевые и отрицательные значения.
См.также
Bag class в Smalltalk.
Запись в Википедии для Multisets.
C++ multisets учебное пособие с примерами.
Математические операции с мультимножествами и варианты их использования описаны в Кнут, Дональд. Искусство компьютерного программирования, том 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()
) данными из iterable. Если значение iterable не указано, новый объект deque будет пустым.Деки - это обобщение стеков и очередей (название произносится как «дека» и является сокращением от «двусторонняя очередь»). Deques поддерживают потокобезопасные операции добавления и извлечения данных с любой стороны deque с экономией памяти с примерно одинаковой производительностью O(1) в любом направлении.
Хотя объекты
list
поддерживают аналогичные операции, они оптимизированы для быстрых операций фиксированной длины и требуют значительных затрат на перемещение в памяти для операцийpop(0)
иinsert(0, v)
, которые изменяют как размер, так и положение о базовом представлении данных.Если значение maxlen не указано или равно
None
, количество добавляемых элементов может увеличиваться до произвольной длины. В противном случае количество добавляемых элементов ограничено указанной максимальной длиной. Как только список с ограниченной длиной заполняется, при добавлении новых элементов соответствующее количество элементов отбрасывается с противоположного конца. Списки с ограниченной длиной предоставляют функциональность, аналогичную фильтруtail
в Unix. Они также полезны для отслеживания транзакций и других массивов данных, где интересны только самые последние действия.Объекты Deque поддерживают следующие методы:
- append(x)¶
Добавьте x к правой части деки.
- appendleft(x)¶
Добавьте x к левой части квадрата.
- 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 в строке (в начале индекса или после него и перед индексом stop). Возвращает первое совпадение или повышает значение
ValueError
, если оно не найдено.Добавлено в версии 3.5.
- insert(i, x)¶
Вставьте x в deque в положении i.
Если вставка приведет к увеличению ограниченного значения deque за пределы 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.
В дополнение к вышесказанному, 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.
Значения ограниченной длины обеспечивают функциональность, аналогичную фильтру tail
в Unix:
def tail(filename, n=10):
'Return the last n lines of a file'
with open(filename) as f:
return deque(f, n)
Другой подход к использованию deques заключается в сохранении последовательности недавно добавленных элементов путем добавления справа и выскакивания слева:
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
round-robin scheduler может быть реализовано с помощью входных итераторов, хранящихся в 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
нарезки и удаления. Например, чисто Python-реализация del d[n]
использует метод rotate()
для позиционирования элементов, которые будут удалены:
def delete_nth(d, n):
d.rotate(-n)
d.popleft()
d.rotate(n)
Чтобы реализовать deque
нарезку, используйте аналогичный подход, применяя rotate()
для переноса целевого элемента в левую часть таблицы. Удалите старые записи с помощью popleft()
, добавьте новые записи с помощью extend()
, а затем поменяйте местами. С небольшими вариациями этого подхода легко реализовать следующие манипуляции со стеком стилей, такие как 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
с ключом в качестве аргумента.Если
default_factory
не являетсяNone
, он вызывается без аргументов, чтобы предоставить значение по умолчанию для данного ключа, это значение вставляется в словарь для ключа и возвращается.Если вызов
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. Новый подкласс используется для создания объектов, похожих на кортежи, которые имеют поля, доступные для поиска по атрибутам, а также индексируемые и повторяемые. Экземпляры подкласса также содержат полезную строку документации (с именем типа и именами полей) и полезный метод
__repr__()
, который выводит содержимое кортежа в форматеname=value
.Имена полей представляют собой последовательность строк, таких как
['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
.значения по умолчанию могут быть
None
или iterable. Поскольку поля со значением по умолчанию должны располагаться после любых полей без значения по умолчанию, значения по умолчанию применяются к крайним правым параметрам. Например, если имена полей['x', 'y', 'z']
, а значения по умолчанию(1, 2)
, тоx
будет обязательным аргументом,y
по умолчанию будет1
, аz
по умолчанию будет равно2
.Если определен module, то атрибуту
__module__
именованного кортежа присваивается это значение.Экземпляры Namedtuple не имеют словарей для каждого экземпляра, поэтому они легкие и требуют не больше памяти, чем обычные кортежи.
Чтобы поддерживать сортировку, именованному классу кортежей следует присвоить переменную, соответствующую typename.
Изменено в версии 3.1: Добавлена поддержка функции переименования.
Изменено в версии 3.6: Параметры verbose и rename стали keyword-only arguments.
Изменено в версии 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, регулярные записи dicts гарантированно будут упорядочены. Если требуются дополнительные функции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)
Поскольку namedtuple является обычным классом 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
может эмулировать Ordereddictod.popitem(last=True)
с помощьюd.popitem()
, который гарантированно приведет к появлению самого правого (последнего) элемента.Обычный
dict
может эмулировать Ordereddictod.popitem(last=False)
с помощью(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
не имеет эффективного эквивалента для Ordereddictod.move_to_end(k, last=False)
, который перемещает ключ и связанное с ним значение в крайнюю левую (первую) позицию.До версии 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: Элементы, ключи и значения views из 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
предоставляют следующий атрибут:
UserList
объекты¶
Этот класс действует как оболочка для объектов list. Это полезный базовый класс для ваших собственных спископодобных классов, которые можно наследовать от них и переопределять существующие методы или добавлять новые. Таким образом, можно добавлять новые модели поведения к спискам.
Необходимость в этом классе была частично устранена возможностью создания подклассов непосредственно из list
; однако с этим классом может быть проще работать, поскольку базовый список доступен в виде атрибута.
- class collections.UserList([list])¶
Класс, имитирующий список. Содержимое экземпляра хранится в обычном списке, доступ к которому осуществляется через атрибут
data
для экземпляровUserList
. Содержимому экземпляра изначально присваивается значение копии list, по умолчанию используется пустой список[]
. list может быть любым повторяемым, например, реальным списком Python или объектомUserList
.В дополнение к поддержке методов и операций с изменяемыми последовательностями, экземпляры
UserList
предоставляют следующий атрибут:
** Требования к подклассам:** Ожидается, что подклассы UserList
будут содержать конструктор, который может быть вызван либо без аргументов, либо с одним аргументом. Операции со списком, возвращающие новую последовательность, пытаются создать экземпляр фактического класса реализации. Для этого предполагается, что конструктор может быть вызван с одним параметром, который представляет собой объект последовательности, используемый в качестве источника данных.
Если производный класс не желает соответствовать этому требованию, все специальные методы, поддерживаемые этим классом, должны быть переопределены; пожалуйста, обратитесь к источникам для получения информации о методах, которые необходимо предоставить в этом случае.
UserString
объекты¶
Класс UserString
действует как оболочка для объектов string. Необходимость в этом классе была частично устранена возможностью создания подклассов непосредственно из str
; однако с этим классом может быть проще работать, поскольку базовая строка доступна в качестве атрибута.
- class collections.UserString(seq)¶
Класс, имитирующий строковый объект. Содержимое экземпляров хранится в обычном строковом объекте, доступ к которому осуществляется с помощью атрибута
data
для экземпляровUserString
. Содержимое экземпляра изначально задается как копия seq. Аргументом seq может быть любой объект, который может быть преобразован в строку с помощью встроенной функцииstr()
.В дополнение к поддержке методов и операций со строками, экземпляры
UserString
предоставляют следующий атрибут:- data¶
Реальный
str
объект, используемый для хранения содержимого классаUserString
.
Изменено в версии 3.5: Новые методы
__getnewargs__
,__rmod__
,casefold
,format_map
,isprintable
, иmaketrans
.