Spec-Zone.ru › Python 3.13

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

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

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

>>> # 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()                       # access the (elem, cnt) pairs
Counter(dict(list_of_pairs))    # convert from a list of (elem, cnt) pairs
c.most_common()[:-n-1:-1]       # n least common elements
+c                              # remove zero and negative counts

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

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

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

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

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

Примечание

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

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

См. также

  • Класс Bag в Smalltalk.
  • Статья Википедии о мультимножествах.
  • Учебник по мультимножествам C++ с примерами.
  • Для математических операций над мультимножествами и их случаев использования см. 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 пустой.

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

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.

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
    # 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 с аргументом key.

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

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

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

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

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

default_factory

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

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

defaultdict Примеры

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

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

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

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

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

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

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

Функция 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__ именованного кортежа устанавливается в это значение.

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

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

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

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

somenamedtuple._fields

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

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

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

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

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

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

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

Для преобразования словаря в именованный кортеж используйте оператор двойной звёздочки (как описано в Распаковка списков аргументов):

>>> 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 collections import OrderedDict
from time import time

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

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

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

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

    """

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

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

Объекты UserDict

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

class collections.UserDict([initialdata])

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

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

data

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

Объекты UserList

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

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

class collections.UserList([list])

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

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

data

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

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

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

Объекты UserString

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

class collections.UserString(seq)

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

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

data

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

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

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

Spec-Zone.ru

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