update ::: сообщение содержит ссылку на ложные утверждения о низком исполнении наборов по сравнению с фризонсетцами. Я утверждаю, что в этом случае по-прежнему разумно использовать фризонсет, хотя нет необходимости хешировать сам набор, только потому, что он более корректен семантически. Хотя на практике я, возможно, не стал бы набирать лишних 6 символов. Я не чувствую себя мотивированным, чтобы пройти и отредактировать сообщение, поэтому просто сообщайте, что «обвинения» ссылаются на некоторые неправильные тесты. Подробности в деталях хэшируются в комментариях. ::: обновление
Второй кусок кода posted Брэндон Крэйг Родос довольно хорошо, но он не ответил на мое предложение об использовании frozenset (ну, не тогда, когда я начал писать это, во всяком случае) , Я собираюсь идти вперед и публиковать его сам.
Вся основа взятого на себя обязательства состоит в том, чтобы проверить, соответствуют ли каждая из ряда значений (L1
) другим набором значений; этот набор значений - это содержимое L2
и L3
. Использование слова «set» в этом предложении говорит: хотя L2
и L3
равны list
, нам не очень нравятся их свойства, подобные спискам, такие как порядок, в котором находятся их значения, или сколько из них содержат. Мы просто заботимся о набор (там он снова) значений, которые они в совокупности содержат.
Если этот набор значений хранится как список, вам необходимо пройти через элементы списка один за другим, проверяя каждый из них. Это относительно много времени, и это плохая семантика: опять же, это «набор» значений, а не список. Таким образом, Python имеет эти опрятные типы наборов, которые содержат кучу уникальных значений и могут быстро сказать вам, есть ли в них какое-то значение или нет. Это работает почти так же, как и типы python dict
, когда вы просматриваете ключ.
Разница между множествами и frozensets является то, что наборы изменчивы, что означает, что они могут быть изменены после создания. Документация по обоим типам - here.
Поскольку набор, который нам нужно создать, объединение значений, хранящихся в L2
и L3
, не будет изменяться после его создания, он семантически подходит для использования неизменяемого типа данных. Это также имеет . Ну, имеет смысл, что у него будет какое-то преимущество; в противном случае, почему Python имеет frozenset
как встроенный?
обновление ...
Брендон ответил на этот вопрос: реальное преимущество замороженных наборов является то, что их неизменность позволяет им быть hashable, что позволяет им быть ключами словаря или членами других наборов ,
Я провел несколько неофициальных тестов времени, сравнивая скорость создания и поиска на относительно больших (3000-элементных) замороженных и изменяемых наборах; не было большой разницы. Это противоречит вышеуказанной ссылке, но поддерживает то, что Брэндон говорит о том, что они идентичны, но для аспекта изменчивости.
... обновление
Теперь, потому что frozensets неизменны, они не имеют способ обновления. Брэндон использовал метод set.update
, чтобы избежать создания и затем отбрасывания временного списка в пути для установки создания; Я собираюсь использовать другой подход.
items = (item for lst in (L2, L3) for item in lst)
Это делает generator expressionitems
итератора над, последовательно, содержимое L2
и L3
. Не только это, но и делает это без создания целого списка, заполненного промежуточными объектами. Использование вложенных выражений for
в генераторах немного запутанно, но мне удается сохранить его в порядке, помня, что они гнездятся в том же порядке, что и если бы вы написали фактические для циклов, например.
def get_items(lists):
for lst in lists:
for item in lst:
yield item
Это generator function эквивалентно выражению генератора, который мы присвоенного items
. Ну, за исключением того, что это определение параметризованной функции вместо прямого назначения переменной.
В любом случае, достаточно отступления. Большое дело с генераторами в том, что они фактически ничего не делают. Ну, по крайней мере, не сразу: они просто настраивают работу, которую нужно выполнить позже, когда выражение генератора повторено. Это формально обозначается как ленивый. Мы собираемся сделать это (ну, во всяком случае, я), передав items
функции frozenset
, которая выполняет итерацию над ней и возвращает морозный холодный морозильник.
unwanted = frozenset(items)
Вы могли бы на самом деле объединить две последние строки, поставив выражение генератора прямо в вызове frozenset
:
unwanted = frozenset(item for lst in (L2, L3) for item in lst)
Это аккуратный синтаксический трюк работает до тех пор, как iterator созданный выражением генератора является единственным параметром функции, которую вы вызываете. В противном случае вы должны записать его в своем обычном отдельном наборе круглых скобок, точно так же, как вы передавали кортеж в качестве аргумента функции.
Теперь мы можем построить новый список так же, как это сделал Брэндон, с list comprehension. Они используют тот же синтаксис, что и выражения генератора, и делают в основном одно и то же, за исключением того, что они являются eager вместо lazy (опять же, это настоящие технические термины), поэтому они получают право на работу с итерацией по элементам и создание список из них.
L4 = [item for item in L1 if item not in unwanted]
Это эквивалентно тому, передавая выражение генератора в list
, например,
L4 = list(item for item in L1 if item not in unwanted)
но более идиоматический.
Так что это будет создавать список L4
, содержащие элементы L1
, которые не были в любом L2
или L3
, поддержание порядка, что они были первоначально в и число их, что там было.
Если вы просто хотите знать, которые значения находятся в L1
, но не в L2
или L3
, это намного проще: просто создать этот набор:
L1_unique_values = set(L1) - unwanted
Вы можете составить список из из этого, as does st0le, но это может быть не совсем то, что вы хотите. Если вы действительно хотите установить значений, которые можно найти только в L1
, вы можете иметь очень веские причины, чтобы держать что набор как set
, или действительно frozenset
:
L1_unique_values = frozenset(L1) - unwanted
... Annnnd, теперь нечто совсем другое:
from itertools import ifilterfalse, chain
L4 = list(ifilterfalse(frozenset(chain(L2, L3)).__contains__, L1))
Существует не один правильный способ сделать это, пока вы не решите, заботитесь ли вы или не хотите дублировать и заказывать. Возможно, какое-то понимание списка или задание работы в зависимости от того, что вам нужно. – istruble
Кроме того, можно ли предположить, что все элементы в списках будут хешироваться все время? Если нет, или иногда нет, это было бы очень значительным. – martineau
Почему вы не используете наборы для начала? Тогда ваша «арифметика» будет работать. – poke