Spec-Zone.ru › Python 3.10

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

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

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

namedtuple()

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

deque

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

ChainMap

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

Counter

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

OrderedDict

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

defaultdict

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

UserDict

обертка вокруг объектов словаря для более удобного создания подклассов словарей

UserList

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

UserString

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

Объекты ChainMap

Введено в версии 3.3.

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

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

class collections.ChainMap(*maps)

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

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

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

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

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

maps

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

new_child(m=None, **kwargs)

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

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

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

parents

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

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

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

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

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

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

См. также

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

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

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

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

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

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

import os, argparse

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

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

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

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

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

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

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

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

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

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

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

Объекты счётчиков

Предоставляется инструмент подсчёта для удобного и быстрого суммирования. Например:

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

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

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

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

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

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

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

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

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

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

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

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

elements()

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

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

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

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

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

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

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

total()

Вычисляет сумму счётчиков.

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

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

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

fromkeys(iterable)

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

update([iterable-or-mapping])

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

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

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

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

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

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

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

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

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

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

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

Примечание

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

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

См. также

  • Класс Bag в Smalltalk.
  • Статья в Википедии о мультимножествах.
  • Учебник по C++ мультимножествам с примерами.
  • Для математических операций над мультимножествами и их случаев использования см. Knuth, Donald. The Art of Computer Programming Volume II, Section 4.6.3, Exercise 19.
  • Для перечисления всех различных мультимножеств заданного размера над заданным набором элементов см. itertools.combinations_with_replacement():

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

объекты deque

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

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

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

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

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

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

append(x)

Добавить x в правую сторону deque.

appendleft(x)

Добавить x в левую сторону deque.

clear()

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

copy()

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

Новая в версии 3.5.

count(x)

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

Новая в версии 3.2.

extend(iterable)

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

extendleft(iterable)

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

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

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

Новая в версии 3.5.

insert(i, x)

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

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

Новая в версии 3.5.

pop()

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

popleft()

Удалить и вернуть элемент с левой стороны deque. Если элементов нет, генерируется IndexError.

remove(value)

Удалить первое вхождение value. Если не найдено, генерируется ValueError.

reverse()

Инвертировать элементы deque на месте и вернуть None.

Новая в версии 3.2.

rotate(n=1)

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

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

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

maxlen

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

Новая в версии 3.1.

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

Начиная с версии 3.5, deque поддерживает __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 Рецепты

Этот раздел демонстрирует различные подходы к работе с deque.

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

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

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

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

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

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

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

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

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

Объекты defaultdict

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

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

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

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

__missing__(key)

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

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

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

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

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

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

default_factory

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

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

defaultdict Примеры

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

classmethod somenamedtuple._make(iterable)

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

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

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

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

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

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

somenamedtuple._replace(**kwargs)

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

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

>>> for partnum, record in inventory.items():
...     inventory[partnum] = record._replace(price=newprices[partnum], timestamp=time.now())
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: Просмотры элементов, ключей и значений видов 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.

© 2001–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.10/library/collections.html

Spec-Zone.ru

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