Spec-Zone.ru › Python 3.14

collections — Типы контейнеров

Исходный код: Lib/collections/__init__.py

Этот модуль реализует специализированные типы контейнеров, предоставляющие альтернативы встроенным контейнерам Python общего назначения: dict, list, set и tuple.

namedtuple()

фабричная функция для создания подклассов кортежей с именованными полями

deque

контейнер, подобный списку, с быстрым добавлением и удалением элементов с обоих концов

ChainMap

класс, подобный словарю, для создания единого представления нескольких отображений

Counter

подкласс словаря для подсчёта хешируемых объектов

OrderedDict

подкласс словаря, сохраняющий порядок добавления записей

defaultdict

подкласс словаря, вызывающий фабричную функцию для предоставления отсутствующих значений

UserDict

обёртка для объектов-словарей, упрощающая создание подклассов словаря

UserList

обёртка для объектов-списков, упрощающая создание подклассов списка

UserString

обёртка для строковых объектов, упрощающая создание подклассов строки

Объекты ChainMap

Добавлено в версии 3.3.

Класс ChainMap позволяет быстро связать несколько отображений, чтобы их можно было рассматривать как единое целое. Это часто намного быстрее, чем создание нового словаря и выполнение нескольких вызовов update().

Класс можно использовать для имитации вложенных областей видимости; он также полезен при создании шаблонов.

class collections.ChainMap(*maps)

Объект ChainMap объединяет несколько словарей или других отображений, создавая единое изменяемое представление. Если параметр maps не указан, создаётся один пустой словарь, поэтому новая цепочка всегда содержит как минимум одно отображение.

Базовые отображения хранятся в списке. Этот список является открытым и доступен или изменяем с помощью атрибута maps. Другого состояния нет.

Поиск последовательно выполняется в базовых отображениях, пока не будет найден ключ. В отличие от этого, операции записи, обновления и удаления выполняются только в первом отображении.

Объект ChainMap включает базовые отображения по ссылке. Поэтому, если одно из базовых отображений обновляется, эти изменения отражаются в ChainMap.

Поддерживаются все обычные методы словаря. Кроме того, имеются атрибут maps, метод для создания новых подчинённых контекстов и свойство для доступа ко всем отображениям, кроме первого:

maps

Список отображений, доступный для изменения пользователем. Список упорядочен от первого проверяемого отображения к последнему. Это единственное хранимое состояние; его можно изменить, чтобы поменять отображения, в которых выполняется поиск. Список всегда должен содержать хотя бы одно отображение.

new_child(m=None, **kwargs)

Возвращает новый объект ChainMap, содержащий новое отображение, за которым следуют все отображения текущего экземпляра. Если указан параметр m, он становится новым отображением в начале списка отображений; если он не указан, используется пустой словарь, поэтому вызов d.new_child() эквивалентен: ChainMap({}, *d.maps). Если указаны именованные аргументы, они обновляют переданное отображение или новый пустой словарь. Этот метод используется для создания подчинённых контекстов, которые можно обновлять, не изменяя значения в родительских отображениях.

Изменено в версии 3.4: Добавлен необязательный параметр m.

Изменено в версии 3.10: Добавлена поддержка именованных аргументов.

parents

Свойство, возвращающее новый объект ChainMap, содержащий все отображения текущего экземпляра, кроме первого. Это удобно, чтобы пропустить первое отображение при поиске. Примеры использования аналогичны случаям применения ключевого слова nonlocal во вложенных областях видимости. Они также схожи со случаями использования встроенной функции super(). Ссылка на d.parents эквивалентна: ChainMap(*d.maps[1:]).

Обратите внимание: порядок итерации объекта ChainMap определяется обходом отображений от последнего к первому:

>>> baseline = {'music': 'bach', 'art': 'rembrandt'}
>>> adjustments = {'art': 'van gogh', 'opera': 'carmen'}
>>> list(ChainMap(adjustments, baseline))
['music', 'art', 'opera']

В результате получается такой же порядок, как при последовательном выполнении вызовов dict.update(), начиная с последнего отображения:

>>> combined = baseline.copy()
>>> combined.update(adjustments)
>>> list(combined)
['music', 'art', 'opera']

Изменено в версии 3.9: Добавлена поддержка операторов | и |=, определённых в PEP 584.

См. также

  • Класс MultiContext из пакета CodeTools от Enthought предоставляет возможности для записи в любое отображение цепочки.
  • Класс Context из Django для создания шаблонов представляет собой цепочку отображений, доступную только для чтения. В нём также предусмотрены операции помещения и извлечения контекстов, аналогичные методу new_child() и свойству parents.
  • В рецепте «Вложенные контексты» можно настроить, применяются ли записи и другие изменения только к первому отображению или к любому отображению цепочки.
  • Значительно упрощённая версия ChainMap, доступная только для чтения.

Примеры и рецепты ChainMap

В этом разделе показаны различные способы работы с цепочками отображений.

Пример имитации внутренней цепочки поиска Python:

import builtins
pylookup = ChainMap(locals(), globals(), vars(builtins))

Пример приоритета аргументов командной строки, заданных пользователем, над переменными среды, которые, в свою очередь, имеют приоритет над значениями по умолчанию:

import os, argparse

defaults = {'color': 'red', 'user': 'guest'}

parser = argparse.ArgumentParser()
parser.add_argument('-u', '--user')
parser.add_argument('-c', '--color')
namespace = parser.parse_args()
command_line_args = {k: v for k, v in vars(namespace).items() if v is not None}

combined = ChainMap(command_line_args, os.environ, defaults)
print(combined['color'])
print(combined['user'])

Примеры использования класса ChainMap для имитации вложенных контекстов:

c = ChainMap()        # Create root context
d = c.new_child()     # Create nested child context
e = c.new_child()     # Child of c, independent from d
e.maps[0]             # Current context dictionary -- like Python's locals()
e.maps[-1]            # Root context -- like Python's globals()
e.parents             # Enclosing context chain -- like Python's nonlocals

d['x'] = 1            # Set value in current context
d['x']                # Get first key in the chain of contexts
del d['x']            # Delete from current context
list(d)               # All nested values
k in d                # Check all nested values
len(d)                # Number of nested values
d.items()             # All nested items
dict(d)               # Flatten into a regular dictionary

Класс ChainMap выполняет обновления (запись и удаление) только в первом отображении цепочки, тогда как поиск выполняется по всей цепочке. Однако, если требуется выполнять запись и удаление во вложенных отображениях, можно создать подкласс, который обновляет ключи, найденные глубже в цепочке:

class DeepChainMap(ChainMap):
    'Variant of ChainMap that allows direct updates to inner scopes'

    def __setitem__(self, key, value):
        for mapping in self.maps:
            if key in mapping:
                mapping[key] = value
                return
        self.maps[0][key] = value

    def __delitem__(self, key):
        for mapping in self.maps:
            if key in mapping:
                del mapping[key]
                return
        raise KeyError(key)

>>> d = DeepChainMap({'zebra': 'black'}, {'elephant': 'blue'}, {'lion': 'yellow'})
>>> d['lion'] = 'orange'         # update an existing key two levels down
>>> d['snake'] = 'red'           # new keys get added to the topmost dict
>>> del d['elephant']            # remove an existing key one level down
>>> d                            # display result
DeepChainMap({'zebra': 'black', 'snake': 'red'}, {}, {'lion': 'orange'})

Объекты Counter

Для удобного и быстрого подсчёта предусмотрен специальный инструмент. Например:

>>> # Tally occurrences of words in a list
>>> cnt = Counter()
>>> for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']:
...     cnt[word] += 1
...
>>> cnt
Counter({'blue': 3, 'red': 2, 'green': 1})

>>> # Find the ten most common words in Hamlet
>>> import re
>>> words = re.findall(r'\w+', open('hamlet.txt').read().lower())
>>> Counter(words).most_common(10)
[('the', 1143), ('and', 966), ('to', 762), ('of', 669), ('i', 631),
 ('you', 554),  ('a', 546), ('my', 514), ('hamlet', 471), ('in', 451)]
class collections.Counter(**kwargs)
class collections.Counter(iterable, /, **kwargs)
class collections.Counter(mapping, /, **kwargs)

Объект Counter — это подкласс dict для подсчёта хешируемых объектов. Это коллекция, в которой элементы хранятся в качестве ключей словаря, а их количество — в качестве значений словаря. Количество может быть любым целым числом, включая ноль и отрицательные числа. Класс Counter подобен мультимножествам и мультимножествам в других языках.

Элементы подсчитываются из итерируемого объекта или инициализируются из другого отображения (или счётчика):

>>> c = Counter()                           # a new, empty counter
>>> c = Counter('gallahad')                 # a new counter from an iterable
>>> c = Counter({'red': 4, 'blue': 2})      # a new counter from a mapping
>>> c = Counter(cats=4, dogs=8)             # a new counter from keyword args

Объекты Counter имеют интерфейс словаря, но для отсутствующих элементов возвращают нулевое количество, а не вызывают исключение KeyError:

>>> c = Counter(['eggs', 'ham'])
>>> c['bacon']                              # count of a missing element is zero
0

Присваивание элементу нулевого количества не удаляет его из счётчика. Чтобы удалить его полностью, используйте del:

>>> c['sausage'] = 0                        # counter entry with a zero count
>>> del c['sausage']                        # del actually removes the entry

Добавлено в версии 3.1.

Изменено в версии 3.7: Как подкласс dict, Counter унаследовал возможность запоминать порядок вставки. Математические операции с объектами Counter также сохраняют порядок. Результаты упорядочиваются по моменту первого появления элемента в левом операнде, а затем — по порядку появления в правом операнде.

Помимо методов, доступных для всех словарей, объекты Counter поддерживают дополнительные методы:

elements()

Возвращает итератор по элементам, повторяя каждый столько раз, сколько указано в его количестве. Элементы возвращаются в порядке первого появления. Если количество элемента меньше единицы, elements() игнорирует его.

>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> sorted(c.elements())
['a', 'a', 'a', 'a', 'b', 'b']
most_common(n=None)

Возвращает список из n наиболее часто встречающихся элементов и их количеств, от наиболее частых к наименее частым. Если n не задано или равно None, most_common() возвращает все элементы счётчика. Элементы с одинаковым количеством упорядочиваются по моменту первого появления:

>>> Counter('abracadabra').most_common(3)
[('a', 5), ('b', 2), ('r', 2)]
subtract(**kwargs)
subtract(iterable, /, **kwargs)
subtract(mapping, /, **kwargs)

Элементы вычитаются из итерируемого объекта или другого отображения (или счётчика). Метод подобен 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(**kwargs)
update(iterable, /, **kwargs)
update(mapping, /, **kwargs)

Элементы подсчитываются из итерируемого объекта или добавляются из другого отображения (или счётчика). Метод подобен 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()                       # access the (elem, cnt) pairs
Counter(dict(list_of_pairs))    # convert from a list of (elem, cnt) pairs
c.most_common()[:-n-1:-1]       # n least common elements
+c                              # remove zero and negative counts

Для объединения объектов Counter предусмотрено несколько математических операций, позволяющих получить мультимножества (счётчики с положительными значениями). Сложение и вычитание объединяют счётчики, складывая или вычитая количества соответствующих элементов. Пересечение и объединение возвращают минимум и максимум соответствующих количеств. Равенство и включение сравнивают соответствующие количества. Каждая операция допускает входные данные с отрицательными количествами, но в результат не включаются значения, равные нулю или меньшие него.

>>> c = Counter(a=3, b=1)
>>> d = Counter(a=1, b=2)
>>> c + d                       # add two counters together:  c[x] + d[x]
Counter({'a': 4, 'b': 3})
>>> c - d                       # subtract (keeping only positive counts)
Counter({'a': 2})
>>> c & d                       # intersection:  min(c[x], d[x])
Counter({'a': 1, 'b': 1})
>>> c | d                       # union:  max(c[x], d[x])
Counter({'a': 3, 'b': 2})
>>> c == d                      # equality:  c[x] == d[x]
False
>>> c <= d                      # inclusion:  c[x] <= d[x]
False

Унарные сложение и вычитание — это сокращённые формы сложения с пустым счётчиком и вычитания из пустого счётчика соответственно.

>>> c = Counter(a=2, b=-4)
>>> +c
Counter({'a': 2})
>>> -c
Counter({'b': 4})

Добавлено в версии 3.3: Добавлена поддержка унарного плюса, унарного минуса и операций над мультимножествами с присваиванием.

Примечание

Счётчики в первую очередь предназначены для работы с положительными целыми числами, представляющими текущие значения подсчёта; однако были приняты меры, чтобы без необходимости не исключать случаи использования других типов или отрицательных значений. Чтобы учесть такие случаи, в этом разделе описаны минимальные ограничения на диапазоны значений и типы.

  • Сам класс Counter является подклассом словаря, не накладывающим ограничений на ключи и значения. Предполагается, что значения — это числа, представляющие количество, но в поле значения можно хранить что угодно.
  • Метод most_common() требует только, чтобы значения можно было упорядочить.
  • Для операций с присваиванием, таких как c[key] += 1, тип значения должен поддерживать только сложение и вычитание. Поэтому подойдут дроби, числа с плавающей точкой и десятичные числа; также поддерживаются отрицательные значения. То же относится к методам update() и subtract(), допускающим отрицательные и нулевые значения как на входе, так и на выходе.
  • Методы работы с мультимножествами предназначены только для случаев с положительными значениями. Входные значения могут быть отрицательными или нулевыми, но создаются только выходные значения, которые больше нуля. Ограничений на типы нет, однако тип значения должен поддерживать сложение, вычитание и сравнение.
  • Метод elements() требует целочисленных значений количества. Он игнорирует нулевые и отрицательные значения.

См. также

  • Класс Bag в Smalltalk.
  • Статья в Википедии о мультимножествах.
  • Руководство по мультимножествам в C++ с примерами.
  • О математических операциях над мультимножествами и случаях их использования см. Кнут, Дональд. Искусство программирования, том II, раздел 4.6.3, упражнение 19.
  • Чтобы перечислить все различные мультимножества заданного размера, составленные из заданного множества элементов, см. itertools.combinations_with_replacement():

    map(Counter, combinations_with_replacement('ABC', 2)) # --> AA AB AC BB BC CC
    

объекты deque

class collections.deque([iterable[, maxlen]])

Возвращает новый объект deque, инициализированный слева направо (с использованием append()) данными из iterable. Если iterable не указан, новый deque пуст.

Деки представляют собой обобщение стеков и очередей (название произносится как «дэк» и является сокращением от «двусторонняя очередь»). Деки обеспечивают потокобезопасное и эффективное с точки зрения памяти добавление и извлечение элементов с любого конца дека с примерно одинаковой производительностью O(1) в обоих направлениях.

Хотя объекты list поддерживают похожие операции, они оптимизированы для быстрых операций фиксированной длины и требуют перемещения памяти затратностью O(n) при операциях pop(0) и insert(0, v), которые изменяют и размер, и положение базового представления данных.

Если maxlen не указан или равен None, деки могут увеличиваться до произвольной длины. В противном случае длина дека ограничена указанным максимальным значением. Когда дек с ограниченной длиной заполнен, при добавлении новых элементов соответствующее количество элементов удаляется с противоположного конца. Деки с ограниченной длиной обеспечивают функциональность, аналогичную фильтру tail в Unix. Они также полезны для отслеживания транзакций и других наборов данных, в которых интерес представляет только самая последняя активность.

Деки являются обобщёнными по типу содержащихся в них элементов.

Объекты deque поддерживают следующие методы:

append(item, /)

Добавляет item в правую часть дека.

appendleft(item, /)

Добавляет item в левую часть дека.

clear()

Удаляет все элементы из дека, оставляя его пустым.

copy()

Создаёт поверхностную копию дека.

Добавлено в версии 3.5.

count(value, /)

Подсчитывает количество элементов дека, равных value.

Добавлено в версии 3.2.

extend(iterable, /)

Расширяет правую часть дека, добавляя элементы из аргумента-итератора.

extendleft(iterable, /)

Расширяет левую часть дека, добавляя элементы из iterable. Обратите внимание: последовательность добавлений слева приводит к обращению порядка элементов в аргументе-итераторе.

index(value[, start[, stop]])

Возвращает позицию value в деке (на индексе start или после него и до индекса stop). Возвращает первое совпадение или вызывает исключение ValueError, если значение не найдено.

Добавлено в версии 3.5.

insert(index, value, /)

Вставляет value в дек на позицию index.

Если при вставке длина дека с ограничением превысит maxlen, вызывается исключение IndexError.

Добавлено в версии 3.5.

pop()

Удаляет и возвращает элемент из правой части дека. Если элементов нет, вызывает исключение IndexError.

popleft()

Удаляет и возвращает элемент из левой части дека. Если элементов нет, вызывает исключение IndexError.

remove(value, /)

Удаляет первое вхождение value. Если значение не найдено, вызывает исключение ValueError.

reverse()

Разворачивает элементы дека на месте и возвращает None.

Добавлено в версии 3.2.

rotate(n=1, /)

Сдвигает дек на n позиций вправо. Если n отрицательно, сдвигает его влево.

Если дек не пуст, сдвиг на одну позицию вправо эквивалентен d.appendleft(d.pop()), а сдвиг на одну позицию влево эквивалентен d.append(d.popleft()).

Объекты deque также предоставляют один атрибут только для чтения:

maxlen

Максимальный размер дека или None, если он не ограничен.

Добавлено в версии 3.1.

Помимо перечисленного выше, деки поддерживают итерацию, сериализацию с помощью pickle, len(d), reversed(d), copy.copy(d), copy.deepcopy(d), проверку вхождения с помощью оператора in и обращения по индексу, например d[0] для доступа к первому элементу. Индексированный доступ на обоих концах выполняется за O(1), но в середине замедляется до O(n). Для быстрого произвольного доступа используйте списки.

Начиная с версии 3.5 деки поддерживают __add__(), __mul__() и __imul__().

Пример:

>>> from collections import deque
>>> d = deque('ghi')                 # make a new deque with three items
>>> for elem in d:                   # iterate over the deque's elements
...     print(elem.upper())
G
H
I

>>> d.append('j')                    # add a new entry to the right side
>>> d.appendleft('f')                # add a new entry to the left side
>>> d                                # show the representation of the deque
deque(['f', 'g', 'h', 'i', 'j'])

>>> d.pop()                          # return and remove the rightmost item
'j'
>>> d.popleft()                      # return and remove the leftmost item
'f'
>>> list(d)                          # list the contents of the deque
['g', 'h', 'i']
>>> d[0]                             # peek at leftmost item
'g'
>>> d[-1]                            # peek at rightmost item
'i'

>>> list(reversed(d))                # list the contents of a deque in reverse
['i', 'h', 'g']
>>> 'h' in d                         # search the deque
True
>>> d.extend('jkl')                  # add multiple elements at once
>>> d
deque(['g', 'h', 'i', 'j', 'k', 'l'])
>>> d.rotate(1)                      # right rotation
>>> d
deque(['l', 'g', 'h', 'i', 'j', 'k'])
>>> d.rotate(-1)                     # left rotation
>>> d
deque(['g', 'h', 'i', 'j', 'k', 'l'])

>>> deque(reversed(d))               # make a new deque in reverse order
deque(['l', 'k', 'j', 'i', 'h', 'g'])
>>> d.clear()                        # empty the deque
>>> d.pop()                          # cannot pop from an empty deque
Traceback (most recent call last):
    File "<pyshell#6>", line 1, in -toplevel-
        d.pop()
IndexError: pop from an empty deque

>>> d.extendleft('abc')              # extendleft() reverses the input order
>>> d
deque(['c', 'b', 'a'])

deque Примеры

В этом разделе показаны различные способы работы с деками.

Деки с ограниченной длиной обеспечивают функциональность, аналогичную фильтру tail в Unix:

def tail(filename, n=10):
    'Return the last n lines of a file'
    with open(filename) as f:
        return deque(f, n)

Ещё один способ использования деков — поддерживать последовательность недавно добавленных элементов, добавляя их справа и извлекая слева:

def moving_average(iterable, n=3):
    # moving_average([40, 30, 50, 46, 39, 44]) --> 40.0 42.0 45.0 43.0
    # https://en.wikipedia.org/wiki/Moving_average
    it = iter(iterable)
    d = deque(itertools.islice(it, n-1))
    d.appendleft(0)
    s = sum(d)
    for elem in it:
        s += elem - d.popleft()
        d.append(elem)
        yield s / n

Планировщик циклического обслуживания можно реализовать, сохраняя входные итераторы в deque. Значения выдаются из активного итератора, находящегося в позиции ноль. Если итератор исчерпан, его можно удалить с помощью popleft(); в противном случае его можно переместить в конец с помощью метода rotate():

def roundrobin(*iterables):
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
    iterators = deque(map(iter, iterables))
    while iterators:
        try:
            while True:
                yield next(iterators[0])
                iterators.rotate(-1)
        except StopIteration:
            # Remove an exhausted iterator.
            iterators.popleft()

Метод rotate() позволяет реализовать срезы и удаление элементов deque. Например, реализация del d[n] на чистом Python использует метод rotate() для перемещения элементов, которые нужно извлечь:

def delete_nth(d, n):
    d.rotate(-n)
    d.popleft()
    d.rotate(n)

Чтобы реализовать срезы deque, примените аналогичный подход: используйте rotate(), чтобы переместить целевой элемент в левую часть дека. Удаляйте старые записи с помощью popleft(), добавляйте новые с помощью extend(), а затем отменяйте сдвиг. Небольшие изменения этого подхода позволяют легко реализовать манипуляции со стеком в стиле Forth, такие как dup, drop, swap, over, pick, rot и roll.

объекты defaultdict

class collections.defaultdict(default_factory=None, /, **kwargs)
class collections.defaultdict(default_factory, mapping, /, **kwargs)
class collections.defaultdict(default_factory, iterable, /, **kwargs)

Возвращает новый объект, подобный словарю. defaultdict является подклассом встроенного класса dict. Он переопределяет один метод и добавляет одну записываемую переменную экземпляра. Остальная функциональность совпадает с функциональностью класса dict и здесь не описана.

Первый аргумент задаёт начальное значение атрибута default_factory; по умолчанию это None. Все остальные аргументы обрабатываются так же, как если бы они были переданы конструктору dict, включая именованные аргументы.

Объекты defaultdict являются обобщёнными по двум типам, обозначающим соответственно типы ключей и значений словаря.

Объекты defaultdict поддерживают следующий метод в дополнение к стандартным операциям dict:

__missing__(key, /)

Если атрибут default_factory равен None, этот метод вызывает исключение KeyError, передавая key в качестве аргумента.

Если default_factory не равен None, он вызывается без аргументов для предоставления значения по умолчанию для данного key; это значение добавляется в словарь для key и возвращается.

Если вызов default_factory вызывает исключение, оно передаётся без изменений.

Этот метод вызывается методом __getitem__() класса dict, когда запрошенный ключ не найден; всё, что он возвращает или вызывает, затем возвращается или вызывается методом __getitem__().

Обратите внимание, что __missing__() не вызывается при выполнении каких-либо операций, кроме __getitem__(). Это означает, что get(), как и для обычных словарей, возвращает None по умолчанию, а не использует default_factory.

Объекты defaultdict поддерживают следующую переменную экземпляра:

default_factory

Этот атрибут используется методом __missing__(); он инициализируется первым аргументом конструктора, если он указан, либо значением None, если аргумент отсутствует.

Изменено в версии 3.9: Добавлены операторы объединения (|) и обновления (|=), описанные в PEP 584.

defaultdict Примеры

Если использовать list в качестве default_factory, последовательность пар ключ-значение легко сгруппировать в словарь списков:

>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> for k, v in s:
...     d[k].append(v)
...
>>> sorted(d.items())
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]

При первом появлении каждого ключа его ещё нет в отображении, поэтому запись автоматически создаётся с помощью функции default_factory, которая возвращает пустой list. Затем операция list.append() добавляет значение в новый список. При повторном появлении ключа поиск выполняется обычным образом (возвращается список для этого ключа), а операция list.append() добавляет в список ещё одно значение. Этот способ проще и быстрее эквивалентного способа с использованием dict.setdefault():

>>> d = {}
>>> for k, v in s:
...     d.setdefault(k, []).append(v)
...
>>> sorted(d.items())
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]

Если установить для default_factory значение int, defaultdict становится удобным для подсчёта элементов (как bag или multiset в других языках):

>>> s = 'mississippi'
>>> d = defaultdict(int)
>>> for k in s:
...     d[k] += 1
...
>>> sorted(d.items())
[('i', 4), ('m', 1), ('p', 2), ('s', 4)]

При первом появлении буквы её ещё нет в отображении, поэтому функция default_factory вызывает int(), чтобы задать начальное значение счётчика, равное нулю. Затем операция инкремента увеличивает счётчик каждой буквы.

Функция int(), которая всегда возвращает ноль, — частный случай функций-констант. Более быстрый и гибкий способ создания функций-констант — использовать лямбда-функцию, которая может возвращать любое постоянное значение, а не только ноль:

>>> def constant_factory(value):
...     return lambda: value
...
>>> d = defaultdict(constant_factory('<missing>'))
>>> d.update(name='John', action='ran')
>>> '%(name)s %(action)s to %(object)s' % d
'John ran to <missing>'

Если установить для default_factory значение set, defaultdict становится удобным для создания словаря множеств:

>>> s = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue', 4)]
>>> d = defaultdict(set)
>>> for k, v in s:
...     d[k].add(v)
...
>>> sorted(d.items())
[('blue', {2, 4}), ('red', {1, 3})]

Фабричная функция namedtuple() для кортежей с именованными полями

Именованные кортежи задают смысл каждой позиции в кортеже и позволяют писать более читаемый, самодокументируемый код. Их можно использовать везде, где используются обычные кортежи; кроме того, они позволяют обращаться к полям по имени, а не по индексу позиции.

collections.namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)

Возвращает новый подкласс кортежа с именем typename. Новый подкласс используется для создания объектов, подобных кортежам: к их полям можно обращаться по атрибутам, а также индексировать их и перебирать. Экземпляры подкласса также имеют полезную строку документации (с typename и field_names) и полезный метод __repr__(), который выводит содержимое кортежа в формате name=value.

field_names — это последовательность строк, например ['x', 'y']. Кроме того, field_names может быть одной строкой, в которой имена полей разделены пробелами и/или запятыми, например 'x y' или 'x, y'.

Для имени поля можно использовать любой допустимый идентификатор Python, кроме имён, начинающихся с подчёркивания. Допустимые идентификаторы состоят из букв, цифр и подчёркиваний, но не начинаются с цифры или подчёркивания и не являются keyword, например class, for, return, global, pass или raise.

Если rename имеет значение true, недопустимые имена полей автоматически заменяются именами, основанными на позициях. Например, ['abc', 'def', 'ghi', 'abc'] преобразуется в ['abc', '_1', 'ghi', '_3'], что устраняет ключевое слово def и повторяющееся имя поля abc.

defaults может быть равен None или представлять собой итерируемый объект со значениями по умолчанию. Поскольку поля со значениями по умолчанию должны следовать после всех полей без значений по умолчанию, значения из defaults применяются к крайним правым параметрам. Например, если имена полей — ['x', 'y', 'z'], а значения по умолчанию — (1, 2), то x будет обязательным аргументом, для y значением по умолчанию будет 1, а для z — 2.

Если задан параметр module, атрибут __module__ именованного кортежа получает это значение.

У экземпляров именованных кортежей нет словарей отдельных экземпляров, поэтому они компактны и требуют не больше памяти, чем обычные кортежи.

Для поддержки сериализации с помощью pickle класс именованного кортежа должен быть присвоен переменной, имя которой совпадает с typename.

Изменено в версии 3.1: Добавлена поддержка rename.

Изменено в версии 3.6: Параметры verbose и rename стали именованными аргументами.

Изменено в версии 3.6: Добавлен параметр module.

Изменено в версии 3.7: Удалены параметр verbose и атрибут _source.

Изменено в версии 3.7: Добавлены параметр defaults и атрибут _field_defaults.

>>> # Basic example
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(11, y=22)     # instantiate with positional or keyword arguments
>>> p[0] + p[1]             # indexable like the plain tuple (11, 22)
33
>>> x, y = p                # unpack like a regular tuple
>>> x, y
(11, 22)
>>> p.x + p.y               # fields also accessible by name
33
>>> p                       # readable __repr__ with a name=value style
Point(x=11, y=22)

Именованные кортежи особенно полезны для присвоения имён полям результирующих кортежей, возвращаемых модулями csv или sqlite3:

EmployeeRecord = namedtuple('EmployeeRecord', 'name, age, title, department, paygrade')

import csv
for emp in map(EmployeeRecord._make, csv.reader(open("employees.csv", "rb"))):
    print(emp.name, emp.title)

import sqlite3
conn = sqlite3.connect('/companydata')
cursor = conn.cursor()
cursor.execute('SELECT name, age, title, department, paygrade FROM employees')
for emp in map(EmployeeRecord._make, cursor.fetchall()):
    print(emp.name, emp.title)

Помимо методов, унаследованных от кортежей, именованные кортежи поддерживают три дополнительных метода и два атрибута. Чтобы избежать конфликтов с именами полей, имена методов и атрибутов начинаются с подчёркивания.

classmethod somenamedtuple._make(iterable, /)

Метод класса, создающий новый экземпляр из существующей последовательности или итерируемого объекта.

>>> t = [11, 22]
>>> Point._make(t)
Point(x=11, y=22)
somenamedtuple._asdict()

Возвращает новый dict, сопоставляющий имена полей с соответствующими значениями:

>>> p = Point(x=11, y=22)
>>> p._asdict()
{'x': 11, 'y': 22}

Изменено в версии 3.1: Возвращает OrderedDict вместо обычного dict.

Изменено в версии 3.8: Возвращает обычный dict вместо OrderedDict. Начиная с Python 3.7, порядок обычных словарей гарантирован. Если требуются дополнительные возможности OrderedDict, рекомендуется преобразовать результат в нужный тип: OrderedDict(nt._asdict()).

somenamedtuple._replace(**kwargs)

Возвращает новый экземпляр именованного кортежа, заменяя указанные поля новыми значениями:

>>> p = Point(x=11, y=22)
>>> p._replace(x=33)
Point(x=33, y=22)

>>> for partnum, record in inventory.items():
...     inventory[partnum] = record._replace(price=newprices[partnum], timestamp=time.now())

Именованные кортежи также поддерживаются универсальной функцией copy.replace().

Изменено в версии 3.13: Для недопустимых именованных аргументов вызывается исключение TypeError вместо ValueError.

somenamedtuple._fields

Кортеж строк со списком имён полей. Полезен для интроспекции и создания новых типов именованных кортежей на основе существующих.

>>> p._fields            # view the field names
('x', 'y')

>>> Color = namedtuple('Color', 'red green blue')
>>> Pixel = namedtuple('Pixel', Point._fields + Color._fields)
>>> Pixel(11, 22, 128, 255, 0)
Pixel(x=11, y=22, red=128, green=255, blue=0)
somenamedtuple._field_defaults

Словарь, сопоставляющий имена полей со значениями по умолчанию.

>>> Account = namedtuple('Account', ['type', 'balance'], defaults=[0])
>>> Account._field_defaults
{'balance': 0}
>>> Account('premium')
Account(type='premium', balance=0)

Чтобы получить поле, имя которого хранится в строке, используйте функцию getattr():

>>> getattr(p, 'x')
11

Чтобы преобразовать словарь в именованный кортеж, используйте оператор с двумя звёздочками (описанный в разделе Распаковка списков аргументов):

>>> d = {'x': 11, 'y': 22}
>>> Point(**d)
Point(x=11, y=22)

Поскольку именованный кортеж является обычным классом Python, с помощью подкласса легко добавить или изменить функциональность. Вот как добавить вычисляемое поле и формат вывода фиксированной ширины:

>>> class Point(namedtuple('Point', ['x', 'y'])):
...     __slots__ = ()
...     @property
...     def hypot(self):
...         return (self.x ** 2 + self.y ** 2) ** 0.5
...     def __str__(self):
...         return 'Point: x=%6.3f  y=%6.3f  hypot=%6.3f' % (self.x, self.y, self.hypot)

>>> for p in Point(3, 4), Point(14, 5/7):
...     print(p)
Point: x= 3.000  y= 4.000  hypot= 5.000
Point: x=14.000  y= 0.714  hypot=14.018

Показанный выше подкласс задаёт для __slots__ пустой кортеж. Это помогает сократить требования к памяти, предотвращая создание словарей экземпляров.

Наследование не подходит для добавления новых сохраняемых полей. Вместо этого просто создайте новый тип именованного кортежа на основе атрибута _fields:

>>> Point3D = namedtuple('Point3D', Point._fields + ('z',))

Строки документации можно настроить, напрямую присвоив значения полям __doc__:

>>> Book = namedtuple('Book', ['id', 'title', 'authors'])
>>> Book.__doc__ += ': Hardcover book in active collection'
>>> Book.id.__doc__ = '13-digit ISBN'
>>> Book.title.__doc__ = 'Title of first printing'
>>> Book.authors.__doc__ = 'List of authors sorted by last name'

Изменено в версии 3.5: Строки документации свойств стали доступны для записи.

См. также

  • См. typing.NamedTuple, чтобы узнать, как добавлять аннотации типов к именованным кортежам. Этот способ также предлагает элегантную запись с использованием ключевого слова class:

    class Component(NamedTuple):
        part_number: int
        weight: float
        description: Optional[str] = None
    
  • См. types.SimpleNamespace() — изменяемое пространство имён на основе словаря, а не кортежа.
  • Модуль dataclasses предоставляет декоратор и функции для автоматического добавления сгенерированных специальных методов в определённые пользователем классы.

Объекты OrderedDict

Упорядоченные словари похожи на обычные словари, но имеют дополнительные возможности для выполнения операций, связанных с упорядочиванием. Сейчас они стали менее важны, поскольку встроенный класс dict научился сохранять порядок вставки (это новое поведение стало гарантированным в Python 3.7).

Тем не менее сохраняются некоторые отличия от dict:

  • Обычный dict был разработан для эффективного выполнения операций отображения. Сохранение порядка вставки имело второстепенное значение.
  • OrderedDict был разработан для эффективного выполнения операций переупорядочивания. Эффективность использования памяти, скорость итерации и производительность операций обновления имели второстепенное значение.
  • Алгоритм OrderedDict лучше справляется с частыми операциями переупорядочивания, чем dict. Как показано в приведённых ниже рецептах, это делает его подходящим для реализации различных типов кэшей LRU.
  • Операция проверки равенства для OrderedDict учитывает порядок элементов.

    Обычный dict может имитировать проверку равенства с учётом порядка с помощью p == q and all(k1 == k2 for k1, k2 in zip(p, q)).

  • У метода popitem() класса OrderedDict другая сигнатура. Он принимает необязательный аргумент, указывающий, какой элемент извлечь.

    Обычный dict может имитировать метод OrderedDict od.popitem(last=True) с помощью d.popitem(), который гарантированно извлекает крайний правый (последний) элемент.

    Обычный dict может имитировать метод OrderedDict od.popitem(last=False) с помощью (k := next(iter(d)), d.pop(k)), который вернёт и удалит крайний левый (первый) элемент, если он существует.

  • В OrderedDict есть метод move_to_end(), позволяющий эффективно перемещать элемент к одному из краёв.

    Обычный dict может имитировать метод OrderedDict od.move_to_end(k, last=True) с помощью d[k] = d.pop(k), который переместит ключ и соответствующее ему значение в крайнюю правую (последнюю) позицию.

    У обычного dict нет эффективного эквивалента метода OrderedDict od.move_to_end(k, last=False), который перемещает ключ и соответствующее ему значение в крайнюю левую (первую) позицию.

  • До Python 3.8 у dict не было метода __reversed__().
class collections.OrderedDict(**kwargs)
class collections.OrderedDict(mapping, /, **kwargs)
class collections.OrderedDict(iterable, /, **kwargs)

Возвращает экземпляр подкласса dict с методами, предназначенными для изменения порядка элементов словаря.

Добавлено в версии 3.1.

popitem(last=True)

Метод popitem() упорядоченных словарей возвращает и удаляет пару (ключ, значение). Пары возвращаются в порядке LIFO, если значение last равно true, или в порядке FIFO, если оно равно false.

move_to_end(key, last=True)

Перемещает существующий key к одному из краёв упорядоченного словаря. Если значение last равно true (по умолчанию), элемент перемещается к правому краю; если значение last равно false — в начало. Если key не существует, возникает исключение KeyError:

>>> d = OrderedDict.fromkeys('abcde')
>>> d.move_to_end('b')
>>> ''.join(d)
'acdeb'
>>> d.move_to_end('b', last=False)
>>> ''.join(d)
'bacde'

Добавлено в версии 3.2.

Помимо обычных методов отображения, упорядоченные словари также поддерживают обратную итерацию с помощью reversed().

Проверка равенства объектов OrderedDict учитывает порядок элементов и примерно эквивалентна list(od1.items())==list(od2.items()).

Проверка равенства объектов OrderedDict и других объектов Mapping не учитывает порядок, как и для обычных словарей. Благодаря этому объекты OrderedDict можно использовать вместо обычного словаря.

Изменено в версии 3.5: Представления представления элементов, ключей и значений объекта OrderedDict теперь поддерживают обратную итерацию с помощью reversed().

Изменено в версии 3.6: После принятия PEP 468 порядок сохраняется для именованных аргументов, передаваемых конструктору OrderedDict и его методу update().

Изменено в версии 3.9: Добавлены операторы слияния (|) и обновления (|=), определённые в PEP 584.

OrderedDict: примеры и рецепты

Несложно создать вариант упорядоченного словаря, который запоминает порядок последней вставки ключей. Если новая запись перезаписывает существующую, исходная позиция вставки изменяется, а запись перемещается в конец:

class LastUpdatedOrderedDict(OrderedDict):
    'Store items in the order the keys were last added'

    def __setitem__(self, key, value):
        super().__setitem__(key, value)
        self.move_to_end(key)

OrderedDict также пригодится для реализации вариантов @functools.lru_cache:

from collections import OrderedDict
from time import monotonic

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 monotonic() - timestamp <= self.maxage:
                return result
        result = self.func(*args)
        self.cache[args] = monotonic(), 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(**kwargs)
class collections.UserDict(mapping, /, **kwargs)
class collections.UserDict(iterable, /, **kwargs)

Класс, имитирующий словарь. Содержимое экземпляра хранится в обычном словаре, доступном через атрибут data экземпляров UserDict. Если переданы аргументы, они используются для инициализации data, как и в случае обычного словаря.

Помимо поддержки методов и операций отображений, экземпляры UserDict предоставляют следующий атрибут:

data

Обычный словарь, используемый для хранения содержимого класса UserDict.

Объекты UserList

Этот класс служит обёрткой для объектов-списков. Он представляет собой полезный базовый класс для собственных классов, подобных спискам: от него можно наследоваться, переопределять существующие методы и добавлять новые. Таким образом можно добавлять спискам новое поведение.

Необходимость в этом классе отчасти отпала благодаря возможности создавать подклассы непосредственно на основе list; однако с этим классом может быть проще работать, поскольку базовый список доступен в виде атрибута.

class collections.UserList([list])

Класс, имитирующий список. Содержимое экземпляра хранится в обычном списке, доступном через атрибут data экземпляров UserList. Изначально содержимое экземпляра является копией list; по умолчанию используется пустой список []. list может быть любым итерируемым объектом, например обычным списком Python или объектом UserList.

Помимо поддержки методов и операций изменяемых последовательностей, экземпляры UserList предоставляют следующий атрибут:

data

Обычный объект list, используемый для хранения содержимого класса 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 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.14/library/collections.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API