1

可能重复:
Python 列表减法运算

在 Python 中,您可以像这样连接列表:

print([3,4,5]+[4,5])

这给出了这个输出:

[3,4,5,4,5]

但我正在寻找的是一个等效的“减法”操作,所以做这样的事情:

print([3,4,5]-[4,5])

会输出这个:

[3]

但是,没有为列表定义减法运算符。我试过这个:

a = [3,4,5]
b = [4,5]
print(list(filter(lambda x : x not in b,a)))

哪个有效,但我不确定这是否是最好的方法。我也想保留原来的物品位置

4

7 回答 7

5

您可以使用列表推导轻松地做到这一点:

nl = [elem for elem in a if elem not in b]

编辑

最好使用 aset来测试。这将从您的列表中删除重复项。

bb= set(b)
nl = [elem for elem in a if elem not in bb]
于 2012-09-26T13:09:23.243 回答
4

这是一个定义不明确的问题。我可以想到列表“减法”的几个非等价定义,其中两个已经被表示:截断(通过切片) - 串联的真正逆;和过滤,类似于集合的“减法”(真正的相对互补)的定义。对于过滤,使用列表推导而不是a转换b为集合是最好的方法。(即larsvegas的回答。)

但尚未考虑的一个版本是减法的多集定义。Python 的Counter类型为我们提供了一个多重集:

>>> from collections import Counter
>>> a = [3, 4, 5]
>>> b = [4, 5]
>>> a_counter = Counter(a)
>>> b_counter = Counter(b)
>>> a_counter
Counter({3: 1, 4: 1, 5: 1})
>>> b_counter
Counter({4: 1, 5: 1})
>>> a_counter - b_counter
Counter({3: 1})

当然,这不会保留顺序,但我们可以通过a基于以下结果进行过滤来解决这个问题a_counter - b_counter

def subtract_lists(a, b):
    multiset_difference = Counter(a) - Counter(b)
    result = []
    for i in a:
        if i in multiset_difference:
            result.append(i)
            multiset_difference -= Counter((i,))
    return result

这有几个不错的属性。它保持秩序;它的作用是串联的真正逆;它在可以包含重复项的数据类型上实现了直观一致的减法版本;它在线性时间内工作。

>>> subtract_lists(a, b)
[3]
>>> subtract_lists([1, 2, 3, 4], [2, 3, 4])
[1]
>>> subtract_lists([1, 2, 3, 4], [2, 4])
[1, 3]
>>> subtract_lists([1, 2, 3, 4, 4, 4], [2, 4])
[1, 3, 4, 4]
于 2012-09-26T13:40:28.303 回答
2
a = [3,4,5]
b = [4,5]

list(set(a) -  set(b))
[3]
于 2012-09-26T13:17:32.057 回答
2

如果您的意思是从列表中删除最后一个元素的减法,那么使用列表切片是一个非常简单的操作:

def list_subtraction(seq, remove):
    l = len(remove)
    if seq[-l:] == remove:
        return seq[:-l]
    else:
        raise ValueError("Subtraction not possible, "
                         "{} is not a tail of {}.".format(remove, seq))
于 2012-09-26T13:19:35.287 回答
1

这当然是因为它只是附加,这就是为什么根本不会删除或影响重复项的原因。

减法只是切掉结尾:

a = [3, 4, 5]
b = [4, 5]
c = a + b

d = c[:-len(b)]

这将使d相等a,即[3, 4, 5]

于 2012-09-26T13:10:28.047 回答
1

鉴于:

a = [3, 4, 5]
b = [4, 5]

然后根据您的需要,以下其中一项应该可以工作。

# remove 'b' from the end of 'a' if it's there (strict de-concatenation)
if a[-len(b):] == b:
    a = a[:-len(b)]

# remove any elements from 'a' that are in `b` (including multiples)
bset = set(b)
a = [x for x in a if x not in bset]

# faster version of above but doesn't preserve order
a = list(set(a) - set(b))

# remove elements from 'a' that are in 'b' (one leftmost item only)
bset = set(b)
a = [x for x in a if x not in bset or bset.remove(x)]

# remove elements from 'a' that are in 'b' (one rightmost item only)
bset = set(b)
a = list(reversed([x for x in reversed(a) if x not in bset or bset.remove(x)]))
于 2012-09-26T14:33:02.273 回答
0

如果您希望它从列表中的任何位置删除内容,并且只删除它们出现在第二个列表中的次数(这样sub([1, 2, 3, 3, 4, 4, 5], [3, 4, 5]) == [1, 2, 3, 4]),您需要稍微复杂一点,并从右边的(副本)中删除每个元素- 使用时的手清单:

def sub(l, r):
    '''
    Remove all elements in r from l
    '''
    r = r[:]
    res = []
    for a in l:
        try:
            i = r.index(a)
        except ValueError:
            res.append(a)
        else:
            del r[i]
    return res

例如,如果你想[1, 2, 3] - [4]成为一个错误,你可以在循环之后检查是否r为非空。

于 2012-09-26T13:37:17.537 回答