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

Источник: Lib/collections/__init__.py


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

namedtuple()

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

deque

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

ChainMap

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

Counter

подкласс dict для подсчета объектов hashable

OrderedDict

подкласс dict, который запоминает порядок добавления записей

defaultdict

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

UserDict

Обертка вокруг объектов словаря для упрощения подклассификации дикты

UserList

Обертка вокруг объектов списков для упрощения их подклассификации

UserString

Обертка вокруг строковых объектов для упрощения подклассификации строк

ChainMap объектов

Added in version 3.3.

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

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

class collections.ChainMap(*maps)

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

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

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

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

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

maps

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

new_child(m=None, **kwargs)

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

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

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

parents

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

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

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

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

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

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

См.также

  • Параметр MultiContext class в Enthought CodeTools package имеет опции для поддержки записи в любой маппинг в цепочке.

  • Метод Context class в Django для шаблонов представляет собой цепочку отображений, доступную только для чтения. В нем также реализовано выталкивание и выпрыгивание контекстов, аналогичное методу new_child() и свойству parents.

  • В параметре Nested Contexts recipe есть опции, позволяющие контролировать, применяются ли записи и другие мутации только к первому отображению или ко всем отображениям в цепочке.

  • A greatly simplified read-only version of 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([iterable-or-mapping])

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

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

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

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

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

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

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

Added in version 3.1.

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

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

elements()

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

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

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

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

Элементы вычитаются из iterable или из другого mapping (или счетчика). Подобно dict.update(), но вычитает счетчики, а не заменяет их. И вход, и выход могут быть нулевыми или отрицательными.

>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> d = Counter(a=1, b=2, c=3, d=4)
>>> c.subtract(d)
>>> c
Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})

Added in version 3.2.

total()

Вычислите сумму подсчетов.

>>> c = Counter(a=10, b=5, c=0)
>>> c.total()
15

Added in version 3.10.

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

fromkeys(iterable)

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

update([iterable-or-mapping])

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

Счетчики поддерживают богатые операторы сравнения для отношений равенства, подмножества и супермножества: ==, !=, <, <=, >, >=. Все эти тесты рассматривают отсутствующие элементы как имеющие нулевое значение, так что Counter(a=1) == Counter(a=1, b=0) возвращает true.

Изменено в версии 3.10: Добавлены богатые операции сравнения.

Изменено в версии 3.10: В тестах на равенство отсутствующие элементы рассматриваются как нулевые. Раньше Counter(a=3) и Counter(a=3, b=0) считались разными.

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

c.total()                       # total of all counts
c.clear()                       # reset all counts
list(c)                         # list unique elements
set(c)                          # convert to a set
dict(c)                         # convert to a regular dictionary
c.items()                       # access the (elem, cnt) pairs
Counter(dict(list_of_pairs))    # convert from a list of (elem, cnt) pairs
c.most_common()[:-n-1:-1]       # n least common elements
+c                              # remove zero and negative counts

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

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

Унарное сложение и вычитание - это быстрые клавиши для добавления пустого счетчика или вычитания из пустого счетчика.

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

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

Примечание

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

  • Сам класс Counter - это подкласс словаря, не имеющий ограничений на ключи и значения. Значениями должны быть числа, представляющие количество, но вы можете хранить в поле значения что угодно.

  • Метод most_common() требует только, чтобы значения были упорядочены.

  • Для операций in-place, таких как c[key] += 1, тип значения должен поддерживать только сложение и вычитание. Таким образом, будут работать дроби, плавающие и десятичные числа, а также поддерживаться отрицательные значения. То же самое справедливо и для операций update() и subtract(), которые допускают отрицательные и нулевые значения как на входе, так и на выходе.

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

  • Метод elements() требует целочисленных отсчетов. Он игнорирует нулевые и отрицательные отсчеты.

См.также

  • Bag class в Smalltalk.

  • Запись в Википедии для Multisets.

  • C++ multisets учебник с примерами.

  • О математических операциях над мультимножествами и их использовании см. в Knuth, Donald. Искусство компьютерного программирования, том II, раздел 4.6.3, упражнение 19.

  • Перечисление всех различных мультимножеств заданного размера над заданным множеством элементов см. в itertools.combinations_with_replacement():

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

deque объектов

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

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

Deques - это обобщение стеков и очередей (название произносится как «дека» и является сокращением от «двусторонняя очередь»). Deques поддерживают потокобезопасные, эффективные с точки зрения памяти добавления и удаления с любой стороны deque с примерно одинаковой O(1) производительностью в любом направлении.

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

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

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

append(x)

Добавьте x в правую часть deque.

appendleft(x)

Добавьте x в левую часть deque.

clear()

Удалите все элементы из deque, оставив его длину 0.

copy()

Создайте неглубокую копию deque.

Added in version 3.5.

count(x)

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

Added in version 3.2.

extend(iterable)

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

extendleft(iterable)

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

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

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

Added in version 3.5.

insert(i, x)

Вставьте x в deque в позицию i.

Если вставка приведет к тому, что ограниченный deque вырастет за пределы maxlen, будет выдано сообщение IndexError.

Added in version 3.5.

pop()

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

popleft()

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

remove(value)

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

reverse()

Переворачивает элементы deque на месте, а затем возвращает None.

Added in version 3.2.

rotate(n=1)

Поверните деку на n шагов вправо. Если n отрицательно, поверните влево.

Когда deque не пуст, поворот на один шаг вправо эквивалентен d.appendleft(d.pop()), а поворот на один шаг влево - d.append(d.popleft()).

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

maxlen

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

Added in version 3.1.

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

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

Пример:

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

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

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

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

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

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

deque Рецепты

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

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

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

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

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

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

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

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

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

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

defaultdict объектов

class collections.defaultdict(default_factory=None, /[, ...])

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

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

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

__missing__(key)

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

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

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

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

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

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

default_factory

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

Изменено в версии 3.9: Добавлены операторы merge (|) и update (|=), указанные в PEP 584.

defaultdict Примеры

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

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

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

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

Установка default_factory в int делает defaultdict полезным для подсчета (как мешок или мультинабор в других языках):

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

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

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

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

Установка default_factory в set делает defaultdict полезным для построения словаря множеств:

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

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

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

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

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

Имена полей представляют собой последовательность строк, например ['x', 'y']. В качестве альтернативы имена_полей могут быть одной строкой с каждым именем поля, разделенным пробелами и/или запятыми, например 'x y' или 'x, y'.

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

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

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

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

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

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

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

Изменено в версии 3.6: Параметры verbose и rename стали keyword-only arguments.

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

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

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

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

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

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

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

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

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

classmethod somenamedtuple._make(iterable)

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

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

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

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

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

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

somenamedtuple._replace(**kwargs)

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

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

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

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

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

somenamedtuple._fields

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

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

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

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

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

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

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

Чтобы преобразовать словарь в именованный кортеж, воспользуйтесь оператором double-star (как описано в Распаковка списков аргументов):

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

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

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

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

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

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

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

Docstrings можно настраивать, делая прямые присваивания полям __doc__:

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

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

См.также

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

    class Component(NamedTuple):
        part_number: int
        weight: float
        description: Optional[str] = None
    
  • См. types.SimpleNamespace(), где описано изменяемое пространство имен, основанное не на кортеже, а на базовом словаре.

  • Модуль dataclasses предоставляет декоратор и функции для автоматического добавления сгенерированных специальных методов в определяемые пользователем классы.

OrderedDict объектов

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

Некоторые отличия от dict все еще остаются:

  • Регулярный dict был разработан для того, чтобы хорошо справляться с картографическими операциями. Отслеживание порядка ввода было вторичным.

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

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

  • Операция равенства для OrderedDict проверяет порядок совпадения.

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

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

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

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

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

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

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

  • До версии Python 3.8 в dict не было метода __reversed__().

class collections.OrderedDict([items])

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

Added in version 3.1.

popitem(last=True)

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

move_to_end(key, last=True)

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

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

Added in version 3.2.

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

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

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

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

Изменено в версии 3.9: Добавлены операторы merge (|) и update (|=), указанные в PEP 584.

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

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

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

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

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

from collections import OrderedDict
from time import time

class TimeBoundedLRU:
    "LRU Cache that invalidates and refreshes old entries."

    def __init__(self, func, maxsize=128, maxage=30):
        self.cache = OrderedDict()      # { args : (timestamp, result)}
        self.func = func
        self.maxsize = maxsize
        self.maxage = maxage

    def __call__(self, *args):
        if args in self.cache:
            self.cache.move_to_end(args)
            timestamp, result = self.cache[args]
            if time() - timestamp <= self.maxage:
                return result
        result = self.func(*args)
        self.cache[args] = time(), result
        if len(self.cache) > self.maxsize:
            self.cache.popitem(last=False)
        return result
class MultiHitLRUCache:
    """ LRU cache that defers caching a result until
        it has been requested multiple times.

        To avoid flushing the LRU cache with one-time requests,
        we don't cache until a request has been made more than once.

    """

    def __init__(self, func, maxsize=128, maxrequests=4096, cache_after=1):
        self.requests = OrderedDict()   # { uncached_key : request_count }
        self.cache = OrderedDict()      # { cached_key : function_result }
        self.func = func
        self.maxrequests = maxrequests  # max number of uncached requests
        self.maxsize = maxsize          # max number of stored return values
        self.cache_after = cache_after

    def __call__(self, *args):
        if args in self.cache:
            self.cache.move_to_end(args)
            return self.cache[args]
        result = self.func(*args)
        self.requests[args] = self.requests.get(args, 0) + 1
        if self.requests[args] <= self.cache_after:
            self.requests.move_to_end(args)
            if len(self.requests) > self.maxrequests:
                self.requests.popitem(last=False)
        else:
            self.requests.pop(args, None)
            self.cache[args] = result
            if len(self.cache) > self.maxsize:
                self.cache.popitem(last=False)
        return result

UserDict объектов

Класс UserDict служит оберткой для объектов словаря. Необходимость в этом классе была частично вытеснена возможностью подклассификации непосредственно из dict; однако с этим классом может быть проще работать, поскольку базовый словарь доступен как атрибут.

class collections.UserDict([initialdata])

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

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

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.