collections — Типы контейнеров данных
Исходный код: Lib/collections/__init__.py
В этом модуле реализованы специализированные типы контейнеров, представляющие собой альтернативу встроенным контейнерам Python общего назначения, dict, list, set и tuple.
функция-фабрика для создания подклассов кортежей с именованными полями | |
список-подобный контейнер с быстрыми добавлением и удалением элементов с обоих концов | |
класс типа dict для создания единого представления нескольких отображений | |
подкласс dict для подсчёта хешируемых объектов | |
подкласс dict, запоминающий порядок добавления элементов | |
подкласс dict, вызывающий функцию-фабрику для предоставления отсутствующих значений | |
обёртка вокруг объектов словарей для более лёгкого создания подклассов словарей | |
обёртка вокруг списков для более лёгкого создания подклассов списков | |
обёртка вокруг объектов строк для более лёгкого создания подклассов строк |
Объекты ChainMap
Добавлен в версии 3.3.
Класс ChainMap предназначен для быстрого объединения нескольких отображений, чтобы их можно было обрабатывать как единое целое. Это часто значительно быстрее, чем создание нового словаря и выполнение нескольких вызовов update().
Класс может использоваться для моделирования вложенных областей видимости и полезен при шаблонизации.
-
class collections.ChainMap(*maps) -
Класс
ChainMapобъединяет несколько словарей или других отображений, чтобы создать единое обновляемое представление. Если maps не указано, предоставляется один пустой словарь, чтобы новая цепочка всегда имела хотя бы одно отображение.Базовые отображения хранятся в списке. Этот список общедоступен и может быть доступен или изменён с помощью атрибута maps. Другого состояния нет.
Поиск ключей происходит последовательно в базовых отображениях, пока ключ не будет найден. В отличие от этого, запись, обновление и удаление операций выполняются только на первом отображении.
Класс
ChainMapвключает базовые отображения по ссылке. Таким образом, если одно из базовых отображений обновляется, эти изменения будут отражены вChainMap.Поддерживаются все обычные методы словарей. Кроме того, есть атрибут maps, метод для создания новых подконтекстов и свойство для доступа ко всем отображениям, кроме первого:
-
maps -
Обновляемый пользователем список отображений. Список упорядочен от первого до последнего отображения в порядке поиска. Это единственное хранимое состояние, и оно может быть изменено для изменения отображений, которые будут просматриваться. В списке всегда должно быть как минимум одно отображение.
-
new_child(m=None, **kwargs) -
Возвращает новый
ChainMap, содержащий новое отображение, за которым следуют все отображения в текущем экземпляре. Еслиmзадано, оно становится новым отображением в начале списка отображений; если нет, используется пустой словарь, так что вызовd.new_child()эквивалентен:ChainMap({}, *d.maps). Если какие-либо ключевые аргументы заданы, они обновляют переданное отображение или новый пустой словарь. Этот метод используется для создания подконтекстов, которые могут быть обновлены без изменения значений в родительских отображениях.Изменено в версии 3.4: Добавлен необязательный параметр
m.Изменено в версии 3.10: Добавлена поддержка ключевых аргументов.
-
parents -
Свойство, возвращающее новый
ChainMap, содержащий все отображения в текущем экземпляре, кроме первого. Это полезно для пропуска первого отображения в поиске. Сценарии использования аналогичны тем, что для ключевого словаnonlocal, используемого во вложенных областях видимости. Сценарии использования также аналогичны тем, что для встроенной функцииsuper(). Ссылка наd.parentsэквивалентна:ChainMap(*d.maps[1:]).
Обратите внимание, что порядок итерации
ChainMapопределяется сканированием отображений справа налево:>>> baseline = {'music': 'bach', 'art': 'rembrandt'} >>> adjustments = {'art': 'van gogh', 'opera': 'carmen'} >>> list(ChainMap(adjustments, baseline)) ['music', 'art', 'opera']Это даёт тот же порядок, что и ряд вызовов
dict.update(), начиная с последнего отображения:>>> combined = baseline.copy() >>> combined.update(adjustments) >>> list(combined) ['music', 'art', 'opera']
Изменено в версии 3.9: Добавлена поддержка операторов
|и|=, описанных в PEP 584. -
См. также
- Класс MultiContext в пакете CodeTools Enthought предоставляет варианты для записи в любое отображение в цепочке.
- Класс Context Django для шаблонизации представляет собой неизменяемую цепочку отображений. Он также имеет функции добавления и удаления контекстов, аналогичные методу
new_child()и свойствуparents. - Рецепт 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'})
Объекты счетчиков
Для удобного и быстрого подсчета предоставляется инструмент счетчика. Например:
>>> # 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Объекты счетчика имеют интерфейс словаря, за исключением того, что для отсутствующих элементов возвращается нулевой счет, а не генерируется исключение
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 также сохраняют порядок. Результаты упорядочиваются в соответствии с тем, когда элемент впервые встречается в левом операнде, а затем — по порядку встречи в правом операнде.Объекты счетчиков поддерживают дополнительные методы, помимо доступных для всех словарей:
-
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++ мультимножества учебник с примерами.
- Для математических операций над мультимножествами и их областей применения см. 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, deques могут расти до произвольной длины. В противном случае 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()).
Объекты deque также предоставляют один атрибут только для чтения:
-
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)
Другой подход к использованию 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
Планировщик циклического обхода круговой очереди можно реализовать с помощью входных итераторов, хранящихся в 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() для перемещения целевого элемента в левую часть 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с ключом в качестве аргумента.Если
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. Новый подкласс используется для создания объектов, похожих на кортежи, которые имеют поля, доступные через поиск по атрибуту, а также индексируемые и итерируемые. Экземпляры подкласса также имеют полезную строку документации (с 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или итерируемым объектом значений по умолчанию. Поскольку поля со значением по умолчанию должны идти после полей без значения по умолчанию, значения по умолчанию применяются к правым параметрам. Например, если имена полей['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
Ordered dictionaries похожи на обычные словари, но имеют дополнительные возможности, связанные с операциями упорядочивания. Они стали менее важными теперь, когда встроенный класс 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()для ordered dictionaries возвращает и удаляет пару (ключ, значение). Пары возвращаются в порядке LIFO, если last имеет значение true, или в порядке FIFO, если false.
-
move_to_end(key, last=True) -
Перемещает существующий ключ в начало или конец ordered dictionary. Элемент перемещается в конец, если 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.
-
В дополнение к обычным методам отображения, ordered dictionaries также поддерживают обратную итерацию с помощью reversed().
Тесты на равенство между объектами OrderedDict чувствительны к порядку и реализуются как list(od1.items())==list(od2.items()). Тесты на равенство между объектами OrderedDict и другими объектами Mapping не учитывают порядок, как и для обычных словарей. Это позволяет использовать объекты OrderedDict везде, где используются обычные словари.
Изменено в версии 3.5: Теперь для представлений items, keys и values в OrderedDict поддерживается обратная итерация с помощью reversed().
Изменено в версии 3.6: С принятием PEP 468, порядок сохраняется для ключевых аргументов, переданных конструктору OrderedDict и его методу update().
Изменено в версии 3.9: Добавлены операторы объединения (|) и обновления (|=), описанные в PEP 584.
Примеры и рецепты OrderedDict
Прямо получить вариант ordered dictionary, который запоминает порядок последней вставки ключей. Если новая запись перезаписывает существующую запись, исходное положение вставки изменяется и перемещается в конец:
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(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–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/library/collections.html