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列表支持什么是做这件事的最好方法?

使用列表理解:

 [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 

但是,如果你不是绝对需要列表属性(例如,sorting),只要使用集合作为其他答案build议。

使用集合差异

 >>> z = list(set(x) - set(y)) >>> z [0, 8, 2, 4, 6] 

或者你可能只有x和y,所以你不必做任何转换。

这是一个“减法”操作。 为此设置数据结构。

在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)中的每个元素进行len(y)项比较,所以它需要二次时间。 quantumSoup的版本使用集合,因此它为x每个元素执行单个常量集合查找,但是因为它将xy转换为集合,所以失去了元素的顺序。

通过只将y转换为一个集合,并按顺序迭代x ,就可以得到两个世界中最好的 – 线性时间和顺序保存。


然而,从QuantumSoup的版本来看,这仍然有一个问题:它需要你的元素是可散列的。 如果你想从另一个字典列表中减去一个字典列表,但是减去的列表很大,你会怎么做?

如果你可以用某种方式装饰你的价值观,那就解决了这个问题。 例如,一个扁平的字典,其值本身就是可散列的:

 ys = {tuple(item.items()) for item in y} [item for item in x if tuple(item.items()) not in ys] 

如果你的types有点复杂(例如,你经常处理JSON兼容的值,可哈希值,列表或字典值recursion相同的types),你仍然可以使用这个解决scheme。 但是有些types不能转换成任何可排列的。


如果你的项目不是,也不可能做成可哈希的,但它们是可比较的,你至less可以得到对数线性时间( O(N*log M) ,这比O(N*M)列表解决scheme的时间,但不如所设置的解决scheme的O(N+M)时间)通过sorting和使用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)] 

如果你的物品既不可排除也不可比较,那么你坚持二次scheme。


*请注意,你也可以通过使用一对OrderedSet对象来做到这一点,你可以find食谱和第三方模块。 但是我觉得这很简单。

**原因集查找是恒定的时间是它所要做的就是散列值,看看是否有一个条目的散列。 如果它不能哈希值,这是行不通的。

尝试这个。

 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 >>> 

在列表中查找值比在列表中查找更快:

 [item for item in x if item not in set(y)] 

我相信这个比例会略微好于:

 [item for item in x if item not in y] 

两者都保持列表的顺序。

@aaronasterling提供的答案看起来不错,但是它与列表的默认接口不兼容: x = MyList(1, 2, 3, 4) vs x = 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))