collections — Контейнерные типы данных

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


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

namedtuple() функция фабрика для создания подклассов кортежей с именованными полями
deque контейнер похожий на список, но с более быстрой вставкой новых элементов в оба конца
ChainMap класс похожий на словарь для создания одного представления для множества отображений
Counter подкласс dict для подсчета хэшируемых объектов
OrderedDict подкласс словаря отслеживающий порядок ключей после их добавления
defaultdict подкласс dict, вызывающий функцию фабрику для предоставления недостающих значений
UserDict обёртка вокруг словарных объектов для облегчения подклассов dict
UserList обёртка вокруг списочных объектов для облегчения подклассов list
UserString обёртка вокруг строковых объектов для облегчения string подклассов

Deprecated since version 3.3, will be removed in version 3.10: Перенесён Коллекции абстрактных базовых классов в модуль collections.abc. Для обратной совместимости, они продолжают быть видимыми в этом модуле до Python 3.9.

Объекты ChainMap

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

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

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

class collections.ChainMap(*maps)

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

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

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

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

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

maps

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

new_child(m=None)

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

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

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']

См.также

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()        # Создать корневой контекст
d = c.new_child()     # Создать вложенный дочерний контекст
e = c.new_child()     # Ребенок c, независимый от d
e.maps[0]             # Текущий контекстный словарь -- подобный Python locals()
e.maps[-1]            # Корневой контекст -- подобный Python globals()
e.parents             # Окружение контекста цепочки -- подобный Python nonlocals

d['x'] = 1            # Установить значение в текущем контексте
d['x']                # Получить первый ключ в цепочке контекстов
del d['x']            # Удалить из текущего контекста
list(d)               # Все вложенные значения
k in d                # Проверить все вложенные значения
len(d)                # Количество вложенных значений
d.items()             # Все вложенные элементы
dict(d)               # Расплющить в обычный словарь

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

class DeepChainMap(ChainMap):
    'Вариант ChainMap, позволяющий напрямую обновлять внутренние области видимости'

    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'         # обновить существующий ключ на два уровня ниже
>>> d['snake'] = 'red'           # новые ключи добавляются в самый верхний словарь
>>> del d['elephant']            # удалите существующий ключ на один уровень ниже
>>> d                            # отображаемые результаты
DeepChainMap({'zebra': 'black', 'snake': 'red'}, {}, {'lion': 'orange'})

Объекты Counter

Инструмент счётчика предоставляет возможность удобного и быстрого подсчёта. Например:

>>> # Подсчет встречаемости слов в списке
>>> cnt = Counter()
>>> for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']:
...     cnt[word] += 1
>>> cnt
Counter({'blue': 3, 'red': 2, 'green': 1})

>>> # Найти десять самых распространенных слов в словаре Гамлета
>>> 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 похож на мешки или мультимножества в других языках.

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

>>> c = Counter()                           # новый, пустой счётчик
>>> c = Counter('gallahad')                 # новый счётчик из итерационного объекта
>>> c = Counter({'red': 4, 'blue': 2})      # новый счётчик из словаря
>>> c = Counter(cats=4, dogs=8)             # новый счётчик из ключевых аргументов args

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

>>> c = Counter(['eggs', 'ham'])
>>> c['bacon']                              # количество отсутствующих элементов равно нулю
0

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

>>> c['sausage'] = 0                        # запись счётчика с нулевым счетом
>>> del c['sausage']                        # Del фактически удаляет запись

Добавлено в версии 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() возвращает элементы all в счётчике. Элементы с равными количествами упорядочиваются в порядке первого попавшегося:

>>> 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})

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

Обычные методы словаря доступны для Counter объектов, за исключением двух, которые для счётчиков работают по-другому.

fromkeys(iterable)

Метод класса не применяется для Counter объектов.

update([iterable-or-mapping])

Элементы подсчитывются от iterable или добавляются в другой mapping (или счётчик). Подобен dict.update(), но добавляет подсчитывание вместо того, чтобы заменить их. Кроме того, iterable предполагает наличие последовательности элементов, а не последовательность пар (key, value).

Общие шаблоны для работы с объектами Counter:

sum(c.values())                 # итого по всем счётчикам
c.clear()                       # сбросить все счётчики
list(c)                         # список уникальных элементов
set(c)                          # конвертировать в множество
dict(c)                         # преобразовать в обычный словарь
c.items()                       # преобразовать список в (elem, cnt) пары
Counter(dict(list_of_pairs))    # конвертировать из списка (elem, cnt) пары
c.most_common()[:-n-1:-1]       # n наименее распространенные элементы
+c                              # удалить ноль и отрицательный счётчики

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

>>> c = Counter(a=3, b=1)
>>> d = Counter(a=1, b=2)
>>> c + d                       # добавить два счётчика вместе:  c[x] + d[x]
Counter({'a': 4, 'b': 3})
>>> c - d                       # вычитать (сохраняя только положительные значения)
Counter({'a': 2})
>>> c & d                       # пересечение:  min(c[x], d[x]) # doctest: +SKIP
Counter({'a': 1, 'b': 1})
>>> c | d                       # объединение:  max(c[x], d[x])
Counter({'a': 3, 'b': 2})

Унарное сложение и вычитание являются ярлыками для добавления пустого счётчика или вычитая из пустого счётчика.

>>> 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.

  • Статья в Wikipedia для Мультимножеств.

  • 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]])

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

Двусторонние очереди являются обобщением стеков и очередей (название произносится как «дек» и является сокращением от «двухсторонняя очередь»). Поддержка двусторонней очереди потокобезопасена и является эффективным по памяти при добавлении элементов с обеих сторон с примерно одинаковой сложности 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.

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

count(x)

Подсчитать количество элементов deque, равное x.

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

extend(iterable)

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

extendleft(iterable)

Расширить левую часть deque, добавляя элементы из 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.

В дополнение к выше сказанному, двусторонняя очередь поддерживает итерации, пиклинг (pickling), 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')                 # Сделать новую очередь с тремя элементами
>>> for elem in d:                   # Перебирать элементы по очереди
...     print(elem.upper())
G
H
I

>>> d.append('j')                    # Добавить новую запись в правую сторону
>>> d.appendleft('f')                # Добавить новую запись в левую сторону
>>> d                                # Показать представление двухсторонней очереди
deque(['f', 'g', 'h', 'i', 'j'])

>>> d.pop()                          # вернуть и удалить самый правый элемент
'j'
>>> d.popleft()                      # вернуть и удалить самый левый элемент
'f'
>>> list(d)                          # Список содержимого очереди
['g', 'h', 'i']
>>> d[0]                             # посмотреть на самый левый элемент
'g'
>>> d[-1]                            # посмотреть на самый правый элемент
'i'

>>> list(reversed(d))                # cписок содержимого очереди наоборот
['i', 'h', 'g']
>>> 'h' in d                         # поиск по очереди
True
>>> d.extend('jkl')                  # Добавление нескольких элементов сразу
>>> d
deque(['g', 'h', 'i', 'j', 'k', 'l'])
>>> d.rotate(1)                      # правое вращение
>>> d
deque(['l', 'g', 'h', 'i', 'j', 'k'])
>>> d.rotate(-1)                     # левое вращение
>>> d
deque(['g', 'h', 'i', 'j', 'k', 'l'])

>>> deque(reversed(d))               # сделать новый deque в обратном порядке
deque(['l', 'k', 'j', 'i', 'h', 'g'])
>>> d.clear()                        # очистить deque
>>> d.pop()                          # невозможность вытолкнуть элемент из пустой 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() меняет порядок ввода
>>> d
deque(['c', 'b', 'a'])

deque рецепты

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

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

def tail(filename, n=10):
    'Возвращает последние n строк файла'
    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
    # http://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 могут быть реализованы с помощью итераторов ввода храниться в 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:
            # Удалить исчерпанный итератор.
            iterators.popleft()

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

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

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

Объекты defaultdict

class collections.defaultdict([default_factory[, ...]])

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

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

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

__missing__(key)

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

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

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

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

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

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

default_factory

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

Примеры 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 полезной для подсчёта (например, пакеты или мультимножество в других языках):

>>> 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() установив счётчик по умолчанию, равное нулю. Операции инкремента затем увеличивает счётчик для каждой буквы.

Функция 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 верно, неверные поля автоматически заменяются позиционными именами. Например, ['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__ именованный кортеж становится равным этому значению.

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

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

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

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

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

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

>>> # Базовый пример
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(11, y=22)     # экземпляр с позиционными или ключевыми аргументами
>>> p[0] + p[1]             # индексируемый как обычный кортеж (11, 22)
33
>>> x, y = p                # распаковать как обычный кортеж
>>> x, y
(11, 22)
>>> p.x + p.y               # поля также доступны по названию
33
>>> p                       # читаемый __repr__ в name=value стиле
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            # просмотр имён полей
('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 провероки соответствия порядка.
  • У метода popitem() из OrderedDict другая сигнатура. Он принимает необязательный аргумент, чтобы указать, какой элемент выталкивается.
  • OrderedDict содержит move_to_end() метод, чтобы эффективно переместить элемент в конечную точку.
  • До Python 3.8, dict не хватало __reversed__() метода.
class collections.OrderedDict([items])

Возвращает сущность dict подкласса, который содержит методы специализированных на перестановке упорядочивания словаря.

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

popitem(last=True)

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

move_to_end(key, last=True)

Переместить существующий key в любой конец упорядоченного словаря. Элемент перемещается в правый конец если last содержит значение true (по умолчанию) или к началу, если last является ложным. Поднимает KeyError если key не существует:

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

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

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

Проверка равенства между объектами OrderedDict чувствительны к порядоку и реализуются как list(od1.items())==list(od2.items()). Проверка равенства между OrderedDict объектов и других Mapping объектов не бесчувстельны к порядку, как обычные словари. Это позволяет заменить OrderedDict объекты, везде, где используется обычные словари.

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

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

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

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

class LastUpdatedOrderedDict(OrderedDict):
    'Хранить элементы в порядке последнего добавления ключей'

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

В OrderedDict также будет полезен для реализации вариантов functools.lru_cache():

class LRU(OrderedDict):
    'Предельный размер, вытесняющий наименьший недавно найденный ключ при заполнении'

    def __init__(self, maxsize=128, /, *args, **kwds):
        self.maxsize = maxsize
        super().__init__(*args, **kwds)

    def __getitem__(self, key):
        value = super().__getitem__(key)
        self.move_to_end(key)
        return value

    def __setitem__(self, key, value):
        if key in self:
            self.move_to_end(key)
        super().__setitem__(key, value)
        if len(self) > self.maxsize:
            oldest = next(iter(self))
            del self[oldest]

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 сущности предоставляют следующие атрибуты:

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.