collections — Container datatypes

Вихідний код: Lib/collections/__init__.py


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

namedtuple()

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

deque

контейнер, схожий на список, із швидким додаванням і вискакуванням на обох кінцях

ChainMap

dict-подібний клас для створення єдиного перегляду кількох відображень

Counter

dict subclass for counting hashable objects

OrderedDict

підклас dict, який запам’ятовує додані записи порядку

defaultdict

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

UserDict

обгортка навколо об’єктів словника для легшого створення підкласів dict

UserList

обгортка навколо об’єктів списку для легшого створення підкласів списку

UserString

обгортка навколо рядкових об’єктів для легшого створення підкласів рядків

ChainMap об’єкти

Нове в версії 3.3.

Клас ChainMap надається для швидкого зв’язування кількох відображень, щоб їх можна було розглядати як єдине ціле. Часто це набагато швидше, ніж створення нового словника та виконання кількох викликів update().

Клас можна використовувати для імітації вкладених областей і корисний у створенні шаблонів.

class collections.ChainMap(*maps)

ChainMap групує кілька dicts або інших відображень разом, щоб створити єдине оновлюване подання. Якщо maps не вказано, надається єдиний порожній словник, щоб новий ланцюжок завжди мав принаймні одне відображення.

Базові відображення зберігаються в списку. Цей список є загальнодоступним, і до нього можна отримати доступ або оновити за допомогою атрибута maps. Іншої держави немає.

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

ChainMap включає базові відображення за посиланням. Отже, якщо одне з базових відображень буде оновлено, ці зміни буде відображено в ChainMap.

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

maps

Список відображень, який можна оновлювати користувачем. Список упорядковано від першого до останнього. Це єдиний стан, який зберігається, і його можна змінити, щоб змінити зіставлення, які шукаються. Список завжди повинен містити принаймні одне зіставлення.

new_child(m=None, **kwargs)

Повертає новий ChainMap, який містить нову карту, за якою слідують усі карти в поточному екземплярі. Якщо вказано m, воно стає новою картою на початку списку зіставлень; якщо не вказано, використовується порожній dict, так що виклик d.new_child() еквівалентний: ChainMap({}, *d.maps). Якщо вказано будь-які аргументи ключового слова, вони оновлюють передану карту або новий порожній dict. Цей метод використовується для створення підконтекстів, які можна оновлювати без зміни значень у будь-якому з батьківських відображень.

Змінено в версії 3.4: Додано необов’язковий параметр m.

Змінено в версії 3.10: Додано підтримку аргументів ключових слів.

parents

Властивість, що повертає новий ChainMap, що містить усі карти в поточному екземплярі, крім першої. Це корисно для пропуску першої карти в пошуку. Випадки використання схожі на випадки використання ключового слова nonlocal, що використовується у вкладених областях. Випадки використання також аналогічні використанню вбудованої функції super(). Посилання на d.parents еквівалентне: ChainMap(*d.maps[1:]).

Note, the iteration order of a ChainMap() is determined by scanning the mappings last to first:

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

Дивись також

ChainMap Приклади та рецепти

У цьому розділі показано різні підходи до роботи з ланцюжковими картами.

Приклад симуляції внутрішнього ланцюжка пошуку Python:

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

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

import os, argparse

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

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

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

Приклади шаблонів використання класу ChainMap для імітації вкладених контекстів:

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

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

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

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

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

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

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

Counter об’єкти

Надається інструмент лічильника для зручного та швидкого підрахунку. Наприклад:

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

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

A Counter is a dict subclass for counting hashable objects. It is a collection where elements are stored as dictionary keys and their counts are stored as dictionary values. Counts are allowed to be any integer value including zero or negative counts. The Counter class is similar to bags or multisets in other languages.

Елементи підраховуються з iterable або ініціалізуються з іншого mapping (або лічильника):

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

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

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

Встановлення лічильника на нуль не видаляє елемент із лічильника. Використовуйте del, щоб видалити його повністю:

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

Нове в версії 3.1.

Змінено в версії 3.7: Як підклас dict, Counter успадкував можливість запам’ятовувати порядок вставки. Математичні операції над об’єктами Counter також зберігають порядок. Результати впорядковуються відповідно до того, коли елемент вперше зустрічається в лівому операнді, а потім у порядку зустрічі в правому операнді.

Об’єкти лічильників підтримують додаткові методи, окрім тих, що доступні для всіх словників:

elements()

Повертає ітератор над елементами, повторюючи кожен стільки разів, скільки його кількість. Елементи повертаються в тому порядку, в якому вони зустрічаються першими. Якщо кількість елемента менше одиниці, elements() проігнорує його.

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

Повертає список з n найпоширеніших елементів і їх підрахунок від найбільш поширених до найменших. Якщо n пропущено або None, most_common() повертає всі елементи в лічильнику. Елементи з рівною кількістю впорядковуються в порядку, коли вони зустрічаються першими:

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

Елементи віднімаються з iterable або з іншого mapping (або лічильника). Подібно до dict.update(), але кількість віднімає, а не замінює. І входи, і виходи можуть бути нульовими або негативними.

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

Нове в версії 3.2.

total()

Обчисліть суму підрахунків.

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

Нове в версії 3.10.

Звичайні методи словника доступні для об’єктів Counter, за винятком двох, які працюють по-різному для лічильників.

fromkeys(iterable)

Цей метод класу не реалізований для об’єктів Counter.

update([iterable-or-mapping])

Елементи підраховуються від iterable або add-in від іншого mapping (або лічильника). Як dict.update(), але додає лічильники замість їх заміни. Крім того, очікується, що iterable буде послідовністю елементів, а не послідовністю пар (ключ, значення).

Лічильники підтримують розширені оператори порівняння для зв’язків рівності, підмножини та надмножини: ==, !=, <, <=, >, >=. Усі ці тести розглядають відсутні елементи як такі, що мають нульову кількість, тому 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() вимагає підрахунку цілих чисел. Він ігнорує нульові та негативні значення.

Дивись також

  • Клас сумки у Smalltalk.

  • Запис у Вікіпедії для Мультимножини.

  • C++ multisets посібник із прикладами.

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

  • Щоб перерахувати всі різні мультимножини заданого розміру над заданим набором елементів, перегляньте itertools.combinations_with_replacement():

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

deque об’єкти

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

Повертає новий об’єкт deque, ініціалізований зліва направо (за допомогою append()) з даними з iterable. Якщо iterable не вказано, нова двочерга порожня.

Deques are a generalization of stacks and queues (the name is pronounced «deck» and is short for «double-ended queue»). Deques support thread-safe, memory efficient appends and pops from either side of the deque with approximately the same O(1) performance in either direction.

Though list objects support similar operations, they are optimized for fast fixed-length operations and incur O(n) memory movement costs for pop(0) and insert(0, v) operations which change both the size and position of the underlying data representation.

Якщо maxlen не вказано або має значення None, дві версії можуть зростати до довільної довжини. В іншому випадку двочерга обмежена до вказаної максимальної довжини. Коли двочерга обмеженої довжини заповнюється, коли додаються нові елементи, відповідна кількість елементів відкидається з протилежного кінця. Обмежена довжина двох рядків забезпечує функціональність, подібну до фільтра tail в Unix. Вони також корисні для відстеження транзакцій та інших наборів даних, де цікаві лише останні дії.

Об’єкти Deque підтримують такі методи:

append(x)

Додайте x до правої сторони дека.

appendleft(x)

Додайте x до лівої сторони дека.

clear()

Видаліть усі елементи з двоканального ряду, залишивши його довжиною 0.

copy()

Створіть поверхневу копію deque.

Нове в версії 3.5.

count(x)

Підрахуйте кількість елементів deque, що дорівнює x.

Нове в версії 3.2.

extend(iterable)

Розширте праву частину двоканального ряду, додавши елементи з ітерованого аргументу.

extendleft(iterable)

Розширте ліву сторону двостороннього коду, додавши елементи з iterable. Зауважте, що серія лівих додань призводить до зміни порядку елементів у повторюваному аргументі.

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

Повертає позицію x у черзі (за індексом start і перед індексом stop). Повертає перший збіг або викликає ValueError, якщо не знайдено.

Нове в версії 3.5.

insert(i, x)

Вставте x у рядок у позицію i.

Якщо вставка призведе до того, що обмежена двочерга виросте за межі maxlen, виникає IndexError.

Нове в версії 3.5.

pop()

Видаліть і поверніть елемент з правого боку двоканальної таблиці. Якщо немає елементів, викликає IndexError.

popleft()

Видаліть і поверніть елемент з лівої сторони двосторонньої версії. Якщо немає елементів, викликає IndexError.

remove(value)

Видаліть перше входження значення. Якщо не знайдено, викликає ValueError.

reverse()

Перевернути елементи дек-версії на місці, а потім повернути None.

Нове в версії 3.2.

rotate(n=1)

Поверніть двічі n кроків праворуч. Якщо n від’ємне, поверніть ліворуч.

Якщо двочерга не порожня, поворот на один крок праворуч еквівалентний d.appendleft(d.pop()) , а поворот на один крок ліворуч еквівалентний d.append(d.popleft ())``.

Об’єкти Deque також надають один атрибут лише для читання:

maxlen

Максимальний розмір двочергового рядка або None, якщо необмежений.

Нове в версії 3.1.

In addition to the above, deques support iteration, pickling, len(d), reversed(d), copy.copy(d), copy.deepcopy(d), membership testing with the in operator, and subscript references such as d[0] to access the first element. Indexed access is O(1) at both ends but slows to O(n) in the middle. For fast random access, use lists instead.

Починаючи з версії 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.

Обмежена довжина двостороннього ряду забезпечує функціональність, подібну до фільтра tail в Unix:

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

Інший підхід до використання двох блоків полягає в підтримці послідовності нещодавно доданих елементів шляхом додавання праворуч і висування ліворуч:

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

Циклічний планувальник може бути реалізований за допомогою ітераторів введення, що зберігаються в deque. Значення виводяться з активного ітератора в нульовій позиції. Якщо цей ітератор вичерпано, його можна видалити за допомогою popleft(); інакше його можна повернути до кінця за допомогою методу rotate():

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

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

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

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

defaultdict об’єкти

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

Повернути новий об’єкт, схожий на словник. defaultdict є підкласом вбудованого класу dict. Він замінює один метод і додає одну змінну екземпляра, доступну для запису. Інші функції такі ж, як і для класу dict, і тут не описані.

Перший аргумент надає початкове значення для атрибута default_factory; за замовчуванням None. Усі решта аргументів обробляються так само, як якщо б вони були передані конструктору dict, включаючи аргументи ключових слів.

Об’єкти defaultdict підтримують наступний метод на додаток до стандартних операцій dict:

__missing__(key)

Якщо атрибут default_factory має значення None, це викликає виключення KeyError з key як аргументом.

Якщо default_factory не є None, він викликається без аргументів, щоб надати значення за замовчуванням для даного ключа, це значення вставляється в словник для ключа та повертається.

Якщо виклик default_factory викликає виняток, цей виняток поширюється без змін.

This method is called by the __getitem__() method of the dict class when the requested key is not found; whatever it returns or raises is then returned or raised by __getitem__().

Note that __missing__() is not called for any operations besides __getitem__(). This means that get() will, like normal dictionaries, return None as a default rather than using 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. Новий підклас використовується для створення кортежних об’єктів, які мають поля, доступні за допомогою пошуку атрибутів, а також які можна індексувати та повторювати. Екземпляри підкласу також мають корисний рядок документації (з назвою типу та іменами полів) і корисний метод __repr__(), який перераховує вміст кортежу у форматі ім'я=значення.

Імена_полів — це послідовність рядків, наприклад ['x', 'y']. Крім того, назви_полів можуть бути одним рядком із назвою кожного поля, розділеного пробілами та/або комами, наприклад 'x y' або 'x, y'.

Для імені поля можна використовувати будь-який дійсний ідентифікатор Python, за винятком імен, що починаються з підкреслення. Дійсні ідентифікатори складаються з літер, цифр і підкреслення, але не починаються з цифри або підкреслення і не можуть бути keyword, таким як клас, для, повернення, глобальний, перехід , або підняти.

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

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

Якщо визначено module, атрибут __module__ названого кортежу встановлюється на це значення.

Іменовані екземпляри кортежу не мають словників для кожного екземпляра, тому вони легкі та не вимагають більше пам’яті, ніж звичайні кортежі.

Щоб підтримувати маринування, іменований клас кортежу має бути призначений змінній, яка відповідає typename.

Змінено в версії 3.1: Додано підтримку перейменування.

Змінено в версії 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, звичайні dicts гарантовано будуть упорядковані. Якщо потрібні додаткові функції 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 і all(k1 == k2 для k1, k2 в zip(p, q)).

  • Метод popitem() OrderedDict має інший підпис. Він приймає необов’язковий аргумент, щоб вказати, який елемент витягується.

    Звичайний dict може емулювати OrderedDict od.popitem(last=True) за допомогою d.popitem(), який гарантовано відкриває крайній правий (останній) елемент.

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

  • OrderedDict має метод move_to_end() для ефективного переміщення елемента в кінцеву точку.

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

    Звичайний dict не має ефективного еквівалента для OrderedDict od.move_to_end(k, last=False), який переміщує ключ і пов’язане з ним значення в крайню ліву (першу) позицію.

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

class collections.OrderedDict([items])

Повертає екземпляр підкласу dict, який має спеціалізовані методи для зміни порядку словника.

Нове в версії 3.1.

popitem(last=True)

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

move_to_end(key, last=True)

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

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

Нове в версії 3.2.

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

Перевірки рівності між об’єктами OrderedDict чутливі до порядку та реалізовані як list(od1.items())==list(od2.items()). Перевірки рівності між об’єктами OrderedDict та іншими об’єктами Mapping не залежать від порядку, як і звичайні словники. Це дозволяє замінювати об’єкти OrderedDict будь-де, де використовується звичайний словник.

Змінено в версії 3.5: Елементи, ключі та значення views OrderedDict тепер підтримують зворотну ітерацію за допомогою reversed().

Змінено в версії 3.6: З прийняттям PEP 468 зберігається порядок для аргументів ключових слів, які передаються конструктору OrderedDict і його методу update().

Змінено в версії 3.9: Додано оператори злиття (|) і оновлення (|=), указані в PEP 584.

OrderedDict Приклади та рецепти

Легко створити впорядкований варіант словника, який запам’ятовує порядок останнього введення ключів. Якщо новий запис перезаписує існуючий запис, початкова позиція вставки змінюється та переміщується в кінець:

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

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

OrderedDict також буде корисним для реалізації варіантів functools.lru_cache():

from time import time

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

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

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

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

    """

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

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

UserDict об’єкти

Клас UserDict діє як оболонка навколо об’єктів словника. Потреба в цьому класі була частково витіснена можливістю створювати підклас безпосередньо з dict; однак з цим класом може бути легше працювати, оскільки базовий словник доступний як атрибут.

class collections.UserDict([initialdata])

Клас, який імітує словник. Вміст екземпляра зберігається у звичайному словнику, який доступний через атрибут data екземплярів UserDict. Якщо надано initialdata, data ініціалізується його вмістом; зауважте, що посилання на ініціальні дані не буде збережено, що дозволить використовувати його для інших цілей.

На додаток до підтримки методів і операцій зіставлення, екземпляри UserDict забезпечують такий атрибут:

data

Справжній словник, який використовується для зберігання вмісту класу UserDict.

UserList об’єкти

Цей клас діє як оболонка навколо об’єктів списку. Це корисний базовий клас для ваших власних спископодібних класів, які можуть успадковувати їх і перевизначати існуючі методи або додавати нові. Таким чином можна додавати нові моделі поведінки до списків.

Потреба в цьому класі була частково витіснена можливістю створювати підкласи безпосередньо з list; однак з цим класом може бути легше працювати, оскільки базовий список доступний як атрибут.

class collections.UserList([list])

Клас, що імітує список. Вміст екземпляра зберігається у звичайному списку, який доступний через атрибут data екземплярів UserList. Вміст екземпляра спочатку встановлено як копія списку, за замовчуванням — порожній список []. 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.