Использование словаря Python в качестве ключа (не вложенный)
Python не позволяет использовать словари в качестве ключей в других словарях. Есть ли обходной путь для использования не вложенных словарей в качестве ключей?
Общая проблема с более сложными объектами без хэширования и с моим конкретным вариантом использования была перенесена сюда. Мое первоначальное описание моего варианта использования было неверным.
10 ответов
Если у вас есть действительно неизменный словарь (хотя мне непонятно, почему вы просто не используете список пар: например, [('content-type', 'text/plain'), ('host', 'example.com')]
), тогда вы можете конвертировать ваши dict
в:
Кортеж пар. Вы уже сделали это в своем вопросе.
tuple
требуется вместоlist
потому что результаты зависят от упорядоченности и неизменности элементов.>>> tuple(sorted(a.items()))
Замороженный набор. Это более подходящий подход с математической точки зрения, поскольку он требует только отношения равенства на элементах вашего неизменяемого
dict
в то время как первый подход требует отношения порядка кроме равенства.>>> frozenset(a.items())
Если бы мне нужно было использовать словари в качестве ключей, я бы выровнял словарь в кортеж кортежей.
Возможно, вам так пригодится такой вопрос: как лучше всего реализовать вложенные словари?
И вот пример уплощенного модуля, который сгладит словари: http://yawpycrypto.sourceforge.net/html/public/Flatten.Flatten-module.html
Я не совсем понимаю ваш вариант использования и подозреваю, что вы пытаетесь преждевременно оптимизировать то, что не требует оптимизации.
Чтобы превратить someDictionary в ключ, сделайте это
key = tuple(sorted(someDictionary .items())
Вы можете легко изменить это с dict( key )
Один из способов сделать это - создать подкласс dict и предоставить метод хеширования. то есть:
class HashableDict(dict):
def __hash__(self):
return hash(tuple(sorted(self.iteritems())))
>>> d = HashableDict(a=1, b=2)
>>> d2 = { d : "foo"}
>>> d2[HashableDict(a=1, b=2)]
"foo"
Однако имейте в виду причины, по которым dicts (или любые изменяемые типы) этого не делают: изменение объекта после его добавления в хеш-таблицу изменит хеш, а это значит, что dict теперь будет содержать его в неправильном сегменте, и поэтому неверные результаты будут возвращены.
Если вы идете по этому пути, либо будьте уверены, что dicts никогда не изменится после того, как они были помещены в другой словарь, либо активно предотвращайте их (например, проверьте, что хеш никогда не изменяется после первого вызова __hash__
и выкинуть исключение если нет.)
Хм, разве ваш случай использования не запоминает вызовы функций? Используя декоратор, вы будете легко поддерживать произвольные функции. И да, они часто перебирают аргументы, и, используя циклические рассуждения, это работает для нестандартных типов, если их можно перебрать.
Посмотрите, например, этот пример памятки
Я подытожу варианты и добавлю один из них, вы можете:
- создать подкласс для dict и предоставить хеш-функцию
- сгладить диктант в кортеж
- мариновать диктант
- преобразовать Dict в строку с помощью модуля json (как показано ниже)
import json
Dict = {'key' :'value123'}
stringifiedDict = json.dumps(Dict)
print(stringifiedDict)
# {"key": "value123"}
newDict = {stringifiedDict: 12345}
print(newDict[stringifiedDict])
# 12345
for key, val in newDict.items():
print(json.loads(key))
# {'key': 'value123'}
print(json.loads(key)['key'])
# value123
Эта функция преобразует вложенный словарь в неизменный набор кортежей, который вы можете использовать в качестве ключа:
def convert_dictionary_tuple(input_dict):
"""
this function receives a nested dictionary and convert it to an immutable tuple of tuples with all the given
dictionary data
:param input_dict: a nested dictionary
:return: immutable tuple of tuples with all the given dictionary data
"""
tuples_dict = {}
for key, value in input_dict.iteritems():
if isinstance(value, dict):
tuples_dict[key] = convert_dictionary_tuple(value)
elif isinstance(value, list):
tuples_dict[key] = tuple([convert_dictionary_tuple(v) if isinstance(v, dict) else v for v in value])
else:
tuples_dict[key] = value
return tuple(sorted(tuples_dict.items()))
Имя класса... ОК :/
Мое решение состоит в том, чтобы создать класс с функциями dict , но реализованный в виде списка с
{key, value}
объекты. тогда ключ и значение могут быть любыми.
class DictKeyDictException(Exception):
pass
class DictKeyDict():
def __init__(self, *args):
values = [self.__create_element(key, value) for key, value in args]
self.__values__ = values
def __setitem__(self, key, value):
self.set(key, value)
def __getitem__(self, key):
return self.get(key)
def __len__(self):
return len(self.__values__)
def __delitem__(self, key):
keys = self.keys()
if key in keys:
index = keys.index(key)
del self.__values__[index]
def clear(self):
self.__values__ = []
def copy(self):
return self.__values__.copy()
def has_key(self, k):
return k in self.keys()
def update(self, *args, **kwargs):
if kwargs:
raise DictKeyDictException(f"no kwargs allowed in '{self.__class__.__name__}.update' method")
for key, value in args:
self[key] = value
return self.__values__
def __repr__(self) -> list:
return repr(self.__values__)
@classmethod
def __create_element(cls, key, value):
return {"key": key, "value": value}
def set(self, key, value) -> None:
keys = self.keys()
if key in keys:
index = keys.index(key)
self.__values__[index] = self.__create_element(key, value)
else:
self.__values__.append(self.__create_element(key, value))
return self.__values__
def keys(self):
return [dict_key_value["key"] for dict_key_value in self.__values__]
def values(self):
return [value["value"] for value in self.__values__]
def items(self):
return [(dict_key_value["key"], dict_key_value["value"]) for dict_key_value in self.__values__]
def pop(self, key, default=None):
keys = self.keys()
if key in keys:
index = keys.index(key)
value = self.__values__.pop(index)["value"]
else:
value = default
return value
def get(self, key, default=None):
keys = self.keys()
if key in keys:
index = keys.index(key)
value = self.__values__[index]["value"]
else:
value = default
return value
def __iter__(self):
return iter(self.keys())
и использование:
dad = {"name": "dad"}
mom = {"name": "mom"}
boy = {"name": "son"}
girl = {"name": "daughter"}
# set
family = DictKeyDict()
family[dad] = {"age": 44}
family[mom] = {"age": 43}
# or
family.set(dad, {"age": 44, "children": [boy, girl]})
# or
family = DictKeyDict(
(dad, {"age": 44, "children": [boy, girl]}),
(mom, {"age": 43, "children": [boy, girl]}),
)
# update
family.update((mom, {"age": 33})) # oups sorry miss /!\ loose my children
family.set({"pet": "cutty"}, "cat")
del family[{"pet": "cutty"}] # cutty left...
family.set({"pet": "buddy"}, "dog")
family[{"pet": "buddy"}] = "wolf" # buddy was not a dog
print(family.keys())
print(family.values())
for k, v in family.items():
print(k, v)
Я не понимаю, почему вы когда-нибудь захотите это сделать, но если вам действительно это нужно, вы можете попробовать выбрать словарь:
mydict = {"a":1, "b":{"c":10}}
import pickle
key = pickle.dumps(mydict)
d[key] = value
Я не знаю, правильно ли я понимаю ваш вопрос, но я попробую
d[repr(a)]=value
Вы можете использовать словарь следующим образом
for el1 in d:
for el2 in eval(el1):
print el2,eval(el1)[el2]