Добавьте словарь в `set()` с объединением

Я только что наткнулся на кое-что интересное, о чем я думал спросить.

Добавляя словарь в set, я предполагал, что словарь будет добавлен как полный словарь, но это не так. Добавляются только ключи:

dicty = {"Key1": "Val1", "Key2": "Val2"}
setunion = set()
setunion.union(dicty)
=> set(['Key2', 'Key1'])

Когда вы пытаетесь добавить его с помощью set.add(), вы получаете сообщение об ошибке:

setadd = set()
setadd.add(dicty)
Traceback (most recent call last):
  File "python", line 1, in <module>
TypeError: unhashable type: 'dict'

Очевидно, такое поведение сильно отличается от списков:

   listy = []
   listy.append(dicty)
   listy
=> [{'Key2': 'Val2', 'Key1': 'Val1'}]

В документах говорится что наборы представляют собой неупорядоченные наборы объектов hashable, что является намеком на некоторые из вышеперечисленных проблем.

Вопросы

Что тут происходит? Элементы набора должны быть хэшируемыми, так что ясно, что это связано с тем, почему я добавляю ключи в набор только с .union(), но почему ошибка с .add()?

Есть ли какая-то причина удобства использования, стоящая за различием в поведении наборов и списков?

Есть ли в Python тип данных (или библиотека), который по существу функционирует как список, но содержит только уникальные элементы?


person NotAnAmbiTurner    schedule 04.12.2015    source источник
comment
Итак, вам нужна структура, сохраняющая порядок, но не допускающая дублирования?   -  person senshin    schedule 05.12.2015
comment
Почему, по-вашему, union можно было добавлять вещи в наборы? union означает создание нового набора, добавление элементов аргумента в новый набор. Это как extend для списков, но не на месте.   -  person user2357112 supports Monica    schedule 05.12.2015
comment
@senshin Я даже не думал о проблеме заказа. Меня больше интересует любая структура, которая позволяет добавлять любой объект, но не допускает (или «поглощает») дубликаты.   -  person NotAnAmbiTurner    schedule 05.12.2015
comment
@NotAnAmbiTurner: это не единственная разница. Настоящей версии add для создания нового набора не существует. Мутативный вариант unionunion_update.   -  person user2357112 supports Monica    schedule 05.12.2015
comment
@ user2357112 Оооооо. Имеет смысл. Я изучаю python, и я возился с hackerrank, который действительно хорош с точки зрения новых концепций, но, возможно, не лучший способ избежать путаницы.   -  person NotAnAmbiTurner    schedule 05.12.2015
comment
@user2357112 user2357112 Но... наборы явно не позволяют добавлять любой объект. Во-первых, они принимают только хеш-таблицы, а для того, чтобы вводить и извлекать диктовки, вам придется делать довольно причудливые вещи.   -  person NotAnAmbiTurner    schedule 05.12.2015


Ответы (2)


Нет, это невозможно по определению. Способ, которым хеш-таблицы (такие как dicts и sets) выполняют поиск, фундаментально уникален по сравнению с тем, как массивы (такие как lists) выполняют поиск. Логическая ошибка заключается в том, что если у вас есть тип данных, который сохраняет только дубликаты, что произойдет, если вы измените один из элементов, чтобы он стал неуникальным?

a, b = [0], [0, 1]
s = SpecialSet(a, b)
a.append(1)  # NOW WHAT?!

Если вы хотите добавить словарь в набор, вы можете добавить его представление dict.items (которое на самом деле представляет собой просто список кортежей), но сначала вы должны выполнить приведение к кортежу.

a = {1:2, 3:4}
s = set()
s.add(tuple(a.items()))

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

for tup in s:
    new_a = dict(tup)

Встроенный тип frozendict был предложен в PEP416, но в конечном итоге был отклонен.

person Adam Smith    schedule 04.12.2015
comment
@NotAnAmbiTurner: Но тогда в вашем SpecialSet есть дубликаты. - person user2357112 supports Monica; 05.12.2015
comment
@user2357112 user2357112 О, я понял. Хорошо спасибо. Позвольте мне попытаться быть более ясным. - person NotAnAmbiTurner; 05.12.2015
comment
Ладно, может я не ясно выразился. Я не хочу хранить изменяемый объект внутри SpecialSet, я думаю, что я ищу возможность для SpecialSet создавать свой собственный экземпляр при передаче изменяемого объекта. Люди используют такую ​​​​изменчивость, чтобы хранить что-то внутри другой объект и изменить его, находясь внутри контейнера? Это просто кажется... неправильным... мне, но я новичок. Разве вы не хотели бы получить значение, возиться с ним и повторно добавить его в контейнер? - person NotAnAmbiTurner; 05.12.2015
comment
Итак, я думаю, моя идея заключалась бы в том, что SpecialSet.__init__ будет копировать и сохранять каждое значение, сначала убедившись, что они уникальны. - person NotAnAmbiTurner; 05.12.2015
comment
@NotAnAmbiTurner Я совсем не понимаю, о чем вы здесь говорите. Чем это отличается от того, что делает set? - person Adam Smith; 05.12.2015
comment
@AdamSmith отличается тем, что наборы принимают только хешируемые объекты. - person NotAnAmbiTurner; 06.12.2015
comment
Предположим, у меня есть дикты какой-то структуры, и только один из ключей должен быть уникальным, как я могу составить такой список. Позвольте мне выразить это иначе. У меня есть словари некоторых пар ключ: значение, только один из ключей неизменен, остальные ключи изменяемы. Мне нужно создать уникальный список таких словарей, что я могу сделать? - person Edik Mkoyan; 21.08.2017
comment
@EdikMkoyan Я не уверен, что ты пытаешься сделать. Можете ли вы задать вопрос об этом с некоторыми конкретными примерами? - person Adam Smith; 21.08.2017
comment
@AdamSmith Мне нужно создать уникальный список словарей, который содержит URL-адреса и некоторую информацию заголовка http, подобную этой. { _id : example.com/bd/en/?preferredCountry=yes , статус: 200, заголовки: {Сервер: Apache-Coyote/1.1, Access-Control-Allow-Origin: example.com, дата: пн, 21 августа 2017 г., 08:52:00 по Гринвичу, соединение: keep-alive, изменение: Accept-Encoding }, источник: example.com } Я переберу этот список и добавлю в mongodb. - person Edik Mkoyan; 22.08.2017

Используя set.union(), попросите добавить в набор элементы аргумента метода, а не сам объект. Итерация по словарю дает вам ключи. Вы получите похожие результаты, если используете set.union() в списке, кортеже или строке, содержимое которых добавляется в набор:

>>> s = {42}
>>> s.union('foo')
set([42, 'o', 'f'])

Были добавлены односимвольные строки 'o' и 'f', а не строка 'foo'.

Вы не можете добавлять словари в набор, потому что они изменяемые; наборы поддерживают только хранение хэшируемых объектов, и одно из требований к объекту, чтобы он был хешируемым, заключается в том, что они неизменны.

person Martijn Pieters    schedule 04.12.2015
comment
nit: вы можете реализовать __hash__ для изменяемого объекта - person Adam Smith; 05.12.2015
comment
@AdamSmith: да, нарушение требования. Python позволяет вам выстрелить себе в ногу. - person Martijn Pieters; 05.12.2015
comment
Моя точка зрения больше связана с поведением. Мне странно, что вы можете добавить диктовку, как диктовку, в список, но не в набор. Сначала я думал, что наборы были разработаны, чтобы быть list уникальными предметами, но, похоже, это нечто большее. - person NotAnAmbiTurner; 05.12.2015
comment
@NotAnAmbiTurner set имеет больше общего с dict, чем с list. Поскольку list ссылается по индексу, ему все равно, что в нем находится. Ссылка set (и ключи dict) по хешу, которая должна быть неразрывно связана со значением. - person Adam Smith; 05.12.2015