Использование словаря Python в качестве ключа (не вложенный)

Python не позволяет использовать словари в качестве ключей в других словарях. Есть ли обходной путь для использования не вложенных словарей в качестве ключей?

Общая проблема с более сложными объектами без хэширования и с моим конкретным вариантом использования была перенесена сюда. Мое первоначальное описание моего варианта использования было неверным.

10 ответов

Решение

Если у вас есть действительно неизменный словарь (хотя мне непонятно, почему вы просто не используете список пар: например, [('content-type', 'text/plain'), ('host', 'example.com')]), тогда вы можете конвертировать ваши dict в:

  1. Кортеж пар. Вы уже сделали это в своем вопросе. tuple требуется вместо list потому что результаты зависят от упорядоченности и неизменности элементов.

    >>> tuple(sorted(a.items()))
    
  2. Замороженный набор. Это более подходящий подход с математической точки зрения, поскольку он требует только отношения равенства на элементах вашего неизменяемого 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]
Другие вопросы по тегам