Spec-Zone.ru › Python 3.8

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

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

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

namedtuple()

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

deque

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

ChainMap

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

Counter

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

OrderedDict

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

defaultdict

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

UserDict

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

UserList

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

UserString

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

Устарело начиная с версии 3.3, будет удалено в версии 3.10: Перемещено Абстрактные базовые классы для коллекций в модуль collections.abc. Для обратной совместимости они по-прежнему видны в этом модуле до Python 3.9.

Объекты ChainMap

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

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

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

class collections.ChainMap(*maps)

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

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

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

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

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

maps

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

new_child(m=None)

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

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

parents

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

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

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

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

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

См. также

  • Класс 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.

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

fromkeys(iterable)

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

update([iterable-or-mapping])

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

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

sum(c.values())                 # 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 = 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, deques могут увеличиваться до произвольной длины. В противном случае 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.

Помимо вышеперечисленного, deques поддерживают итерацию, сериализацию, 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 Рецепты

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

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)

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

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

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

def roundrobin(*iterables):
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
    iterators = deque(map(iter, iterables))
    while iterators:
        try:
            while True:
                yield next(iterators[0])
                iterators.rotate(-1)
        except StopIteration:
            # 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[, ...]])

Возвращает новый объект, подобный словарю. 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, если он отсутствует.

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

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

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

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

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

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

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

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

>>> # 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 проверяет соответствие порядка.
  • Метод popitem() класса OrderedDict имеет другую сигнатуру. Он принимает необязательный аргумент для указания удаляемого элемента.
  • Класс OrderedDict имеет метод move_to_end() для эффективного перемещения элемента в конец.
  • До Python 3.8 в классе dict отсутствовал метод __reversed__().
class collections.OrderedDict([items])

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

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

popitem(last=True)

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

move_to_end(key, last=True)

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

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

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

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

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

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

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

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

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

class LastUpdatedOrderedDict(OrderedDict):
    '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():

class LRU(OrderedDict):
    'Limit size, evicting the least recently looked-up key when full'

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

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

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

Объекты UserDict

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

class collections.UserDict([initialdata])

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

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

data

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

Объекты UserList

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

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

class collections.UserList([list])

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

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

data

Реальный объект list, используемый для хранения содержимого класса UserList.

Требования к подклассам: Подклассы UserList должны предлагать конструктор, который можно вызывать либо без аргументов, либо с одним аргументом. Операции со списками, возвращающие новую последовательность, пытаются создать экземпляр фактического класса реализации. Для этого предполагается, что конструктор можно вызвать с одним параметром – объектом последовательности, используемым в качестве источника данных.

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

Объекты UserString

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

class collections.UserString(seq)

Класс, имитирующий строковый объект. Содержимое экземпляра хранится в обычном строковом объекте, доступном через атрибут data экземпляров UserString. Содержимое экземпляра изначально устанавливается как копия seq. Аргумент seq может быть любым объектом, который можно преобразовать в строку с помощью встроенной функции str().

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

data

Реальный объект str, используемый для хранения содержимого класса UserString.

Изменено в версии 3.5: Новые методы __getnewargs__, __rmod__, casefold, format_map, isprintable и maketrans.

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

Spec-Zone.ru

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