collections
— Типы данных контейнеров¶
Источник: Lib/collections/__init__.py
Этот модуль реализует специализированные типы данных контейнеров, которые являются альтернативой встроенным контейнерам общего назначения Python, dict
, list
, set
и tuple
.
фабричная функция для создания подклассов кортежей с именованными полями |
|
Контейнер, похожий на список, с быстрым добавлением и выгрузкой с обоих концов |
|
диктоподобный класс для создания единого представления нескольких отображений |
|
подкласс dict для подсчета объектов hashable |
|
подкласс dict, который запоминает порядок добавления записей |
|
подкласс дикты, который вызывает функцию фабрики для предоставления недостающих значений |
|
Обертка вокруг объектов словаря для упрощения подклассификации дикты |
|
Обертка вокруг объектов списков для упрощения их подклассификации |
|
Обертка вокруг строковых объектов для упрощения подклассификации строк |
ChainMap
объектов¶
Added in version 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. Это коллекция, в которой элементы хранятся как ключи словаря, а их количество - как значения словаря. Счетчики могут быть любыми целыми значениями, включая нулевые или отрицательные. КлассCounter
похож на мешки или мультинаборы в других языках.Элементы считаются из iterable или инициализируются из другого mapping (или счетчика):
>>> 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
Added in version 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])¶
Элементы вычитаются из iterable или из другого 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})
Added in version 3.2.
- total()¶
Вычислите сумму подсчетов.
>>> c = Counter(a=10, b=5, c=0) >>> c.total() 15
Added in version 3.10.
Для объектов
Counter
доступны обычные методы словаря, за исключением двух, которые работают по-другому для счетчиков.- update([iterable-or-mapping])¶
Элементы подсчитываются из iterable или добавляются из другого 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() # 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})
Added in version 3.3: Добавлена поддержка унарных плюсов, унарных минусов и операций с мультимножествами на месте.
Примечание
Счетчики в первую очередь были разработаны для работы с положительными целыми числами, чтобы представлять текущие подсчеты; тем не менее, было принято решение не исключать случаи, когда требуются другие типы или отрицательные значения. Чтобы помочь в решении этих задач, в этом разделе описаны минимальные ограничения на диапазон и тип.
Сам класс
Counter
- это подкласс словаря, не имеющий ограничений на ключи и значения. Значениями должны быть числа, представляющие количество, но вы можете хранить в поле значения что угодно.Метод
most_common()
требует только, чтобы значения были упорядочены.Для операций in-place, таких как
c[key] += 1
, тип значения должен поддерживать только сложение и вычитание. Таким образом, будут работать дроби, плавающие и десятичные числа, а также поддерживаться отрицательные значения. То же самое справедливо и для операцийupdate()
иsubtract()
, которые допускают отрицательные и нулевые значения как на входе, так и на выходе.Методы мультисетов предназначены только для случаев использования с положительными значениями. Входные данные могут быть отрицательными или нулевыми, но создаются только выходные данные с положительными значениями. Ограничений на тип нет, но тип значения должен поддерживать сложение, вычитание и сравнение.
Метод
elements()
требует целочисленных отсчетов. Он игнорирует нулевые и отрицательные отсчеты.
См.также
Bag class в Smalltalk.
Запись в Википедии для Multisets.
C++ multisets учебник с примерами.
О математических операциях над мультимножествами и их использовании см. в Knuth, Donald. Искусство компьютерного программирования, том 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 - это обобщение стеков и очередей (название произносится как «дека» и является сокращением от «двусторонняя очередь»). Deques поддерживают потокобезопасные, эффективные с точки зрения памяти добавления и удаления с любой стороны deque с примерно одинаковой O(1) производительностью в любом направлении.
Хотя объекты
list
поддерживают аналогичные операции, они оптимизированы для быстрых операций с фиксированной длиной и несут O(n) затрат на перемещение памяти для операцийpop(0)
иinsert(0, v)
, которые изменяют как размер, так и положение базового представления данных.Если maxlen не указан или равен
None
, дек может расти до произвольной длины. В противном случае deque ограничивается указанной максимальной длиной. Когда deque ограниченной длины заполнен, при добавлении новых элементов соответствующее количество элементов удаляется с противоположного конца. Дескрипторы ограниченной длины обеспечивают функциональность, аналогичную фильтруtail
в Unix. Они также полезны для отслеживания транзакций и других пулов данных, где интерес представляет только самая последняя активность.Объекты Deque поддерживают следующие методы:
- append(x)¶
Добавьте x в правую часть deque.
- appendleft(x)¶
Добавьте x в левую часть deque.
- clear()¶
Удалите все элементы из deque, оставив его длину 0.
- copy()¶
Создайте неглубокую копию deque.
Added in version 3.5.
- count(x)¶
Подсчитайте количество элементов deque, равных x.
Added in version 3.2.
- extend(iterable)¶
Расширяет правую часть deque, добавляя элементы из аргумента iterable.
- extendleft(iterable)¶
Расширьте левую часть deque, добавляя элементы из iterable. Обратите внимание, что серия добавлений слева приводит к изменению порядка элементов в аргументе iterable.
- index(x[, start[, stop]])¶
Возвращает позицию x в deque (в или после индекса start и до индекса stop). Возвращает первое совпадение или выводит
ValueError
, если оно не найдено.Added in version 3.5.
- insert(i, x)¶
Вставьте x в deque в позицию i.
Если вставка приведет к тому, что ограниченный deque вырастет за пределы maxlen, будет выдано сообщение
IndexError
.Added in version 3.5.
- pop()¶
Удаляет и возвращает элемент из правой части deque. Если элементов нет, выдает сообщение
IndexError
.
- popleft()¶
Удаляет и возвращает элемент из левой части deque. Если элементов нет, выдает сообщение
IndexError
.
- remove(value)¶
Удалите первое вхождение значения. Если значение не найдено, выдает ошибку
ValueError
.
- reverse()¶
Переворачивает элементы deque на месте, а затем возвращает
None
.Added in version 3.2.
- rotate(n=1)¶
Поверните деку на n шагов вправо. Если n отрицательно, поверните влево.
Когда deque не пуст, поворот на один шаг вправо эквивалентен
d.appendleft(d.pop())
, а поворот на один шаг влево -d.append(d.popleft())
.
Объекты Deque также предоставляют один атрибут, доступный только для чтения:
- maxlen¶
Максимальный размер deque или
None
, если он не ограничен.Added in version 3.1.
Кроме того, deques поддерживают итерацию, pickling, 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
Рецепты¶
В этом разделе показаны различные подходы к работе с деками.
Деки ограниченной длины обеспечивают функциональность, аналогичную фильтру 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
В 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
нарезки и удаления. Например, чистая реализация del d[n]
на Python опирается на метод rotate()
для позиционирования элементов, которые должны быть вырезаны:
def delete_nth(d, n):
d.rotate(-n)
d.popleft()
d.rotate(n)
Чтобы реализовать нарезку deque
, используйте аналогичный подход, применяя rotate()
, чтобы перенести целевой элемент в левую часть deque. Удалите старые записи с помощью 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
с аргументом key.Если
default_factory
не являетсяNone
, вызывается без аргументов, чтобы предоставить значение по умолчанию для заданного ключа, это значение вставляется в словарь для ключа и возвращается.Если вызов
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()
, чтобы установить нулевой счет по умолчанию. Затем операция инкремента наращивает счет для каждой буквы.
Функция 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
.Имена полей представляют собой последовательность строк, например
['x', 'y']
. В качестве альтернативы имена_полей могут быть одной строкой с каждым именем поля, разделенным пробелами и/или запятыми, например'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
или iterable значений по умолчанию. Поскольку поля со значением по умолчанию должны идти после полей без значения по умолчанию, значения по умолчанию применяются к самым правым параметрам. Например, если имена полей равны['x', 'y', 'z']
, а значения по умолчанию равны(1, 2)
, тоx
будет обязательным аргументом,y
будет значением по умолчанию для1
, аz
будет значением по умолчанию для2
.Если определен module, то атрибут
__module__
именованного кортежа устанавливается в это значение.Экземпляры именованных кортежей не имеют словарей для каждого экземпляра, поэтому они легковесны и требуют не больше памяти, чем обычные кортежи.
Для поддержки pickling именованный класс кортежа должен быть присвоен переменной, соответствующей typename.
Изменено в версии 3.1: Добавлена поддержка переименования.
Изменено в версии 3.6: Параметры verbose и rename стали keyword-only arguments.
Изменено в версии 3.6: Добавлен параметр модуль.
Изменено в версии 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
Чтобы преобразовать словарь в именованный кортеж, воспользуйтесь оператором double-star (как описано в Распаковка списков аргументов):
>>> 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',))
Docstrings можно настраивать, делая прямые присваивания полям __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
, методы которого специализируются на перестановке порядка словарей.Added in version 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'
Added in version 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: Добавлены операторы merge (|
) и update (|=
), указанные в 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
предоставляют следующий атрибут:
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
.