Python не имеет встроенного типа frozendict. Оказывается, это не будет полезно слишком часто (хотя, вероятно, это будет полезно чаще, чем frozenset
есть).
Наиболее распространенная причина, по которой такой тип нужен, - это запоминание вызовов функций для функций с неизвестными аргументами. Наиболее распространенное решение для хранения хешируемого эквивалента dict (где значения могут быть хешируемыми) выглядит примерно так tuple(sorted(kwargs.iteritems()))
.
Это зависит от того, что сортировка не слишком сумасшедшая. Python не может положительно обещать, что сортировка приведет к чему-то разумному здесь. (Но это не может обещать многого другого, поэтому не переживайте слишком сильно.)
Вы могли бы довольно легко сделать какую-то обертку, которая работает очень похоже на диктовку. Это может выглядеть примерно так
import collections
class FrozenDict(collections.Mapping):
"""Don't forget the docstrings!!"""
def __init__(self, *args, **kwargs):
self._d = dict(*args, **kwargs)
self._hash = None
def __iter__(self):
return iter(self._d)
def __len__(self):
return len(self._d)
def __getitem__(self, key):
return self._d[key]
def __hash__(self):
# It would have been simpler and maybe more obvious to
# use hash(tuple(sorted(self._d.iteritems()))) from this discussion
# so far, but this solution is O(n). I don't know what kind of
# n we are going to run into, but sometimes it's hard to resist the
# urge to optimize when it will gain improved algorithmic performance.
if self._hash is None:
hash_ = 0
for pair in self.items():
hash_ ^= hash(pair)
self._hash = hash_
return self._hash
Должно работать отлично:
>>> x = FrozenDict(a=1, b=2)
>>> y = FrozenDict(a=1, b=2)
>>> x is y
False
>>> x == y
True
>>> x == {'a': 1, 'b': 2}
True
>>> d = {x: 'foo'}
>>> d[y]
'foo'
__hash__
метод может быть немного улучшен. Просто используйте временную переменную при вычислении хеша, и устанавливайте ее только тогда,self._hash
когда вы получите окончательное значение. Таким образом, другой поток, получающий хэш во время вычисления первого, будет просто выполнять избыточный расчет, а не получать неправильное значение.Любопытно, что хотя
frozenset
в Python мы редко используем , все еще нет замороженных карт. Идея была отвергнута в PEP 416. - Добавить встроенный тип frozendict . Идея может быть пересмотрена в Python 3.9, см. PEP 603. Добавление типа замороженной карты в коллекции .Итак, решение Python 2 для этого:
Все еще кажется несколько отстойным
В Python3 у вас есть возможность этого :
Теперь конфигурация по умолчанию может обновляться динамически, но оставаться неизменной там, где вы хотите, чтобы она была неизменной, передавая прокси вместо этого.
Таким образом, изменения в
default_config
обновлении будут обновлены,DEFAULTS
как и ожидалось, но вы не можете записать в сам прокси-объект сопоставления.По общему признанию, это не совсем то же самое, что «неизменный, легко изменяемый диктат», - но это достойный заменитель, учитывая те же варианты использования, для которых мы могли бы хотеть заморозить.
источник
def foo(config=MappingProxyType({'a': 1})):
? Ваш пример все еще позволяет глобальную модификацию черезdefault_config
.config = default_config = {'a': 1}
- это опечатка.Если предположить, что ключи и значения словаря сами по себе неизменны (например, строки), то:
источник
dict(t)
Нет
fronzedict
, но вы можете использоватьMappingProxyType
то, что было добавлено в стандартную библиотеку с Python 3.3:источник
TypeError: can't pickle mappingproxy objects
MappingProxyType
до сих пор не решаема.Вот код, который я использовал. Я подкласс заморозил. Преимущества этого следующие.
Обновление 21 января 2015: оригинальный фрагмент кода, который я разместил в 2014 году, использовал цикл for для поиска подходящего ключа. Это было невероятно медленно. Теперь я собрал реализацию, которая использует возможности хеширования frozenset. Пары ключ-значение хранятся в специальных контейнерах, где
__hash__
и__eq__
функции , основанные только на ключе. Этот код также был официально протестирован модульно, в отличие от того, что я разместил здесь в августе 2014 года.Лицензия в стиле MIT.
источник
Item
чтобы быть хэшем ключа - это аккуратный хак!diff(diff({key}))
все еще линейно по размеру FrozenDict, в то время как обычное время доступа к dict постоянно в среднем случае.Я думаю о frozendict каждый раз, когда пишу такую функцию:
источник
optional_dict_parm = optional_dict_parm or {}
types.MappingProxyType
({})
Вы можете использовать
frozendict
изutilspie
пакета как:Согласно документу :
источник
Установить Frozendict
Используй это!
источник
Да, это мой второй ответ, но это совершенно другой подход. Первая реализация была на чистом питоне. Этот в Cython. Если вы знаете, как использовать и компилировать модули Cython, это так же быстро, как обычный словарь. Примерно от 0,04 до 0,06 микросекунды для получения одного значения.
Это файл "frozen_dict.pyx"
Вот файл "setup.py"
Если у вас установлен Cython, сохраните два вышеуказанных файла в одном каталоге. Перейдите в этот каталог в командной строке.
И ты должен быть готов.
источник
Основным недостатком
namedtuple
является то, что он должен быть указан перед использованием, поэтому он менее удобен для случаев одноразового использования.Тем не менее, существует практический обходной путь, который можно использовать для обработки многих таких случаев. Допустим, вы хотите иметь неизменный эквивалент следующего слова:
Это можно подражать так:
Можно даже написать вспомогательную функцию для автоматизации этого:
Конечно, это работает только для плоских диктовок, но не должно быть слишком сложно реализовать рекурсивную версию.
источник
getattr(fa, x)
вместо тогоfa[x]
, ни одинkeys
метод на кончиках пальцев, и все другие причины , отображение может быть желательным.подклассов
dict
Я вижу эту модель в дикой природе (GitHub) и хотел упомянуть об этом:
пример использования:
Pros
get()
,keys()
,items()
(iteritems()
на PY2) и все вкусности изdict
из коробки без явного их реализацииdict
что означает производительность (dict
написано в c в CPython)isinstance(my_frozen_dict, dict)
возвращает True - хотя python поощряет использование утилит во многих пакетахisinstance()
, это может сэкономить множество настроек и настроекCons
__hash__
немного быстрее.источник
__setitem__
и наследованиеdict
безумно быстро по сравнению со многими альтернативами.Другим вариантом является
MultiDictProxy
класс изmultidict
пакета.источник
Мне нужно было получить доступ к фиксированным ключам для чего-то, что-то вроде глобально-постоянного вида вещей, и я остановился на чем-то вроде этого:
Используйте это как
ПРЕДУПРЕЖДЕНИЕ: я не рекомендую это для большинства случаев использования, поскольку это делает некоторые довольно серьезные компромиссы.
источник
При отсутствии поддержки родного языка вы можете сделать это самостоятельно или использовать существующее решение. К счастью, Python значительно упрощает расширение их базовых реализаций.
источник