Операция вычитания списка Python
Я хочу сделать что-то похожее на это:
>>> x = [1,2,3,4,5,6,7,8,9,0]
>>> x
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
>>> y = [1,3,5,7,9]
>>> y
[1, 3, 5, 7, 9]
>>> y - x # (should return [2,4,6,8,0])
но это не поддерживается списками python
Каков наилучший способ сделать это?
10 ответов:
использовать список понимание:
[item for item in x if item not in y]если вы хотите использовать
-синтаксис инфикса, вы можете просто сделать:class MyList(list): def __init__(self, *args): super(MyList, self).__init__(args) def __sub__(self, other): return self.__class__(*[item for item in self if item not in other])затем вы можете использовать его как:
x = MyList(1, 2, 3, 4) y = MyList(2, 5, 2) z = x - yно если вам абсолютно не нужны свойства списка (например, порядок), просто используйте наборы, как рекомендуют другие ответы.
использовать установить разницу
>>> z = list(set(x) - set(y)) >>> z [0, 8, 2, 4, 6]или у вас могут быть только X и y, поэтому вам не нужно делать никаких преобразований.
это операция" set subtraction". Используйте для этого заданную структуру данных.
В Python 2.7:
x = {1,2,3,4,5,6,7,8,9,0} y = {1,3,5,7,9} print x - yвыход:
>>> print x - y set([0, 8, 2, 4, 6])
Если дублировать и заказывать детали проблема:
[i for i in a if not i in b or b.remove(i)]a = [1,2,3,3,3,3,4] b = [1,3] result: [2, 3, 3, 3, 4]
для многих случаев использования, ответ вы хотите:
ys = set(y) [item for item in x if item not in ys]это гибрид между aaronasterling это и ответ quantumSoup.
версия aaronasterling делает
len(y)сравнение элементов для каждого элемента вx, так что это занимает квадратичное время. версия quantumSoup использует наборы, поэтому она выполняет один поиск набора констант для каждого элемента вx-но, потому что он преобразует иxиyв наборы, он теряет порядок ваших элементов.путем преобразования только
yв набор, и переборxдля того, чтобы вы получили лучшее из обоих миров-линейное время и сохранение порядка.*
однако у этого все еще есть проблема с версией quantumSoup: она требует, чтобы ваши элементы были хэшируемыми. Это в значительной степени встроено в природу наборов.** Если вы пытаетесь, например, вычесть список диктов из другого списка диктов, но список чтобы вычесть большой, что вы делаете?
если вы можете украсить ваши ценности в некотором роде, что они hashable, что решает проблему. Например, с плоским словарем, значения которого сами хешируются:
ys = {tuple(item.items()) for item in y} [item for item in x if tuple(item.items()) not in ys]если видов немного более сложных (например, часто вы имеете дело с JSON-совместимые значения, которые hashable, или списки или словарь, значения которых являются рекурсивно тот же тип), вы все еще можете использовать это решение. Но некоторые типы просто не могут быть преобразуется во что-нибудь хэшируемое.
если ваши элементы не, и не может быть сделано, hashable, но они сопоставимы, можно как минимум получить лог-линейного времени (
O(N*log M), что намного лучше, чемO(N*M)время решения списка, но не так хорошо, какO(N+M)время заданного решения) путем сортировки и использованияbisect:ys = sorted(y) def bisect_contains(seq, item): index = bisect.bisect(seq, item) return index < len(seq) and seq[index] == item [item for item in x if bisect_contains(ys, item)]
если ваши товары не hashable, ни сравнима, то вы застряли с квадратичной решение.
* обратите внимание, что вы также можете сделать это с помощью пары
OrderedSetобъекты, для которых можно найти рецепты и сторонних модулей. Но я думаю, что это проще.** причина, по которой поиск настроек является постоянным временем, заключается в том, что все, что ему нужно сделать, это хэшировать значение и посмотреть, есть ли запись для этого хэша. Если он не может хэшировать значение, это не будет работать.
поиск значений в наборах выполняется быстрее, чем поиск их в списках:
[item for item in x if item not in set(y)]Я считаю, что это будет масштаб чуть лучше:
[item for item in x if item not in y]оба сохраняют порядок списков.
попробуйте это.
def subtract_lists(a, b): """ Subtracts two lists. Throws ValueError if b contains items not in a """ # Terminate if b is empty, otherwise remove b[0] from a and recurse return a if len(b) == 0 else [a[:i] + subtract_lists(a[i+1:], b[1:]) for i in [a.index(b[0])]][0] >>> x = [1,2,3,4,5,6,7,8,9,0] >>> y = [1,3,5,7,9] >>> subtract_lists(x,y) [2, 4, 6, 8, 0] >>> x = [1,2,3,4,5,6,7,8,9,0,9] >>> subtract_lists(x,y) [2, 4, 6, 8, 0, 9] #9 is only deleted once >>>
ответ, предоставленный @aaronasterling, выглядит хорошо, однако он не совместим с интерфейсом по умолчанию list:
x = MyList(1, 2, 3, 4)vsx = MyList([1, 2, 3, 4]). Таким образом, приведенный ниже код может быть использован в качестве более дружественного python-list:class MyList(list): def __init__(self, *args): super(MyList, self).__init__(*args) def __sub__(self, other): return self.__class__([item for item in self if item not in other])пример:
x = MyList([1, 2, 3, 4]) y = MyList([2, 5, 2]) z = x - y
в этом примере вычитаются два списка:
# List of pairs of points list = [] list.append([(602, 336), (624, 365)]) list.append([(635, 336), (654, 365)]) list.append([(642, 342), (648, 358)]) list.append([(644, 344), (646, 356)]) list.append([(653, 337), (671, 365)]) list.append([(728, 13), (739, 32)]) list.append([(756, 59), (767, 79)]) itens_to_remove = [] itens_to_remove.append([(642, 342), (648, 358)]) itens_to_remove.append([(644, 344), (646, 356)]) print("Initial List Size: ", len(list)) for a in itens_to_remove: for b in list: if a == b : list.remove(b) print("Final List Size: ", len(list))
Я думаю, что это быстрее:
In [1]: a = [1,2,3,4,5] In [2]: b = [2,3,4,5] In [3]: c = set(a) ^ set(b) In [4]: c Out[4]: {1}
Comments