Подтвердить что ты не робот

Какова цель коллекций. ChainMap?

В Python 3.3 класс ChainMap был добавлен в модуль collections:

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

Пример:

>>> from collections import ChainMap
>>> x = {'a': 1, 'b': 2}
>>> y = {'b': 10, 'c': 11}
>>> z = ChainMap(y, x)
>>> for k, v in z.items():
        print(k, v)
a 1
c 11
b 10

Он был мотивирован этой проблемой и опубликован этим (не было создано PEP).

Насколько я понимаю, это альтернатива наличию дополнительного словаря и поддержание его с помощью update().

Вопросы:

  • В каких случаях используется ChainMap?
  • Есть ли примеры реального мира ChainMap?
  • Используется ли в сторонних библиотеках, которые переключаются на python3?

Бонусный вопрос: есть ли способ использовать его на Python2.x?


Я слышал об этом в Transforming Code into Beautiful, Idiomatic Python Обсуждение PyCon Раймондом Хеттингером, и я хотел бы добавить его в свой инструментарий, но мне не хватает понимания, когда я должен использовать его.

4b9b3361

Ответ 1

Мне нравятся примеры @b4hand, и я действительно использовал в предыдущих структурах, подобных ChainMap (но не сам ChainMap) для двух целей, которые он упоминает: переопределение многоуровневых конфигураций и эмуляция переменных стека/области.

Я хотел бы указать на две другие мотивации/преимущества/отличия ChainMap, по сравнению с использованием цикла dict-update, таким образом сохраняя только "окончательную" версию:

  • Дополнительная информация:, поскольку структура ChainMap является "многоуровневой", она поддерживает ответ на вопрос: "Получаю ли значение" по умолчанию "или переопределенное? Что такое исходное значение (значение по умолчанию)? На каком уровне значение было переопределено (заимствование @b4hand config example: user-config или command-line-overrides)? Используя простой dict, информация, необходимая для ответа на эти вопросы, уже потеряна.

  • Компромисс скорости: предположим, что у вас есть N уровни и не более M ключей в каждом, построение ChainMap занимает O(N) и каждый поиск O(N) худший случай [ *], а построение dict с использованием цикла update принимает O(NM) и каждый поиск O(1). Это означает, что если вы создаете часто и выполняете только несколько запросов каждый раз, или если M является большим, подход Lazy-construction ChainMap работает в вашу пользу.

[*] Анализ в (2) предполагает, что dict-access имеет значение O(1), когда на самом деле это O(1) в среднем и O(M) наихудший случай. Подробнее здесь.

Ответ 2

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

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

Стандартные библиотечные документы для ChainMap приводят несколько примеров и ссылок на аналогичные реализации в сторонних библиотеках. В частности, он называет Djangos класс контекста и Enthought класс MultiContext.

Ответ 3

Я взломаю это:

Chainmap выглядит как очень-то вроде абстракции. Это хорошее решение для очень специализированной проблемы. Я предлагаю этот вариант использования.

Если у вас есть:

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

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

Но это все после-факта оправдание. Ребята из Python столкнулись с проблемой, придумали хорошее решение в контексте своего кода, затем сделали дополнительную работу, чтобы отвлечь свое решение, чтобы мы могли использовать его, если захотим. Больше власти им. Но зависит ли это от вашей проблемы, чтобы решить вашу проблему.

Ответ 4

Чтобы не ответить на ваш вопрос:

Бонусный вопрос: есть ли способ использовать его на Python2.x?

from ConfigParser import _Chainmap as ChainMap

Однако имейте в виду, что это не реальный ChainMap, он наследует от DictMixin и определяет только:

__init__(self, *maps)
__getitem__(self, key)
keys(self)

# And from DictMixin:
__iter__(self)
has_key(self, key)
__contains__(self, key)
iteritems(self)
iterkeys(self)
itervalues(self)
values(self)
items(self)
clear(self)
setdefault(self, key, default=None)
pop(self, key, *args)
popitem(self)
update(self, other=None, **kwargs)
get(self, key, default=None)
__repr__(self)
__cmp__(self, other)
__len__(self)

Его реализация также не кажется особенно эффективной.