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

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


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

namedtuple()

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

deque

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

ChainMap

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

Counter

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

OrderedDict

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

defaultdict

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

UserDict

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

UserList

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

UserString

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

ChainMap объекты

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

  • В Django Context class для шаблонов используется цепочка отображений, доступная только для чтения. В нем также есть функции выталкивания и выпрыгивания контекстов, аналогичные методу 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 для подсчета хешируемых объектов. Это коллекция, в которой элементы хранятся в виде ключей словаря, а их количество - в виде значений словаря. Счетчики могут быть любыми целыми значениями, включая нулевые или отрицательные счетчики. Класс 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

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

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

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

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

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

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

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

elements()

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

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

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

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

Элементы вычитаются из 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.

total()

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

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

Добавлено в версии 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()                       # convert to a list of (elem, cnt) pairs
Counter(dict(list_of_pairs))    # convert from a list of (elem, cnt) pairs
c.most_common()[:-n-1:-1]       # n least common elements
+c                              # remove zero and negative counts

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

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

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

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

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

Примечание

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

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

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

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

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

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

См.также

  • Bag 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, deques может вырасти до произвольной длины. В противном случае deque ограничивается указанной максимальной длиной. Когда deque ограниченной длины заполнен, при добавлении новых элементов с противоположного конца отбрасывается соответствующее количество элементов. Деки ограниченной длины обеспечивают функциональность, аналогичную фильтру tail в Unix. Они также полезны для отслеживания транзакций и других пулов данных, где интерес представляет только самая последняя активность.

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

append(x)

Добавьте x к правой части deque.

appendleft(x)

Добавьте x к левой части deque.

clear()

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

copy()

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

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

count(x)

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

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

extend(iterable)

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

extendleft(iterable)

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

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

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

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

insert(i, x)

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

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

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

pop()

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

popleft()

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

remove(value)

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

reverse()

Перевернуть элементы deque на месте, а затем вернуть None.

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

rotate(n=1)

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

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

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

maxlen

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

Добавлено в версии 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)

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

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

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] опирается на метод rotate() для позиционирования элементов, которые будут извлечены:

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

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

defaultdict объекты

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

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

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

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

__missing__(key)

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

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

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

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

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

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

default_factory

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

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

defaultdict Примеры

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Имена полей представляют собой последовательность строк, например ['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())
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 может эмулировать od.popitem(last=True) OrderedDict с помощью d.popitem(), который гарантированно выводит самый правый (последний) элемент.

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

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

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

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

  • До Python 3.8 в dict отсутствовал метод __reversed__().

class collections.OrderedDict([items])

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

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

popitem(last=True)

Метод popitem() для упорядоченных словарей возвращает и удаляет пару (ключ, значение). Пары возвращаются в порядке 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'

Добавлено в версии 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: Добавлены операторы слияния (|) и обновления (|=), указанные в 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 time import time

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

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

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

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

    """

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

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

UserDict объекты

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

class collections.UserDict([initialdata])

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

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

data

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

UserList объекты

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

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

class collections.UserList([list])

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

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

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.

Вернуться на верх