我在Python中有两个列表:

temp1 = ['One', 'Two', 'Three', 'Four']
temp2 = ['One', 'Two']

假设每个列表中的元素都是唯一的,我想用第一个列表中的项创建第三个列表,这些项不在第二个列表中:

temp3 = ['Three', 'Four']

有没有没有周期和检查的快速方法?


当前回答

我们可以计算交集减去列表并集:

temp1 = ['One', 'Two', 'Three', 'Four']
temp2 = ['One', 'Two', 'Five']

set(temp1+temp2)-(set(temp1)&set(temp2))

Out: set(['Four', 'Five', 'Three']) 

其他回答

可以使用python的XOR运算符来完成。

这将删除每个列表中的重复项 这将显示temp1与temp2和temp2与temp1的差异。


set(temp1) ^ set(temp2)

如果您想要更类似于变更集的东西……可以使用Counter

from collections import Counter

def diff(a, b):
  """ more verbose than needs to be, for clarity """
  ca, cb = Counter(a), Counter(b)
  to_add = cb - ca
  to_remove = ca - cb
  changes = Counter(to_add)
  changes.subtract(to_remove)
  return changes

lista = ['one', 'three', 'four', 'four', 'one']
listb = ['one', 'two', 'three']

In [127]: diff(lista, listb)
Out[127]: Counter({'two': 1, 'one': -1, 'four': -2})
# in order to go from lista to list b, you need to add a "two", remove a "one", and remove two "four"s

In [128]: diff(listb, lista)
Out[128]: Counter({'four': 2, 'one': 1, 'two': -1})
# in order to go from listb to lista, you must add two "four"s, add a "one", and remove a "two"

下面是@arkolec的回答,下面是一个用于比较列表、元组和集的实用程序类:

from difflib import SequenceMatcher

class ListDiffer:

    def __init__(self, left, right, strict:bool=False):
        assert isinstance(left, (list, tuple, set)), "left must be list, tuple or set"
        assert isinstance(right, (list, tuple, set)), "right must be list, tuple or set"
        self.l = list(left) if isinstance(left, (tuple, set)) else left
        self.r = list(right) if isinstance(left, (tuple, set)) else right

        if strict:
            assert isinstance(left, right.__class__), \
                f'left type ({left.__class__.__name__}) must equal right type ({right.__class__.__name__})'

        self.diffs = []
        self.equal = []

        for tag, i, j, k, l in SequenceMatcher(None, self.l, self.r).get_opcodes():
            if tag in ['delete', 'replace', 'insert']:
                self.diffs.append((tag, i, j, k, l))
            elif tag == 'equal':
                [self.equal.append(v) for v in left[i:j]]
                


    def has_diffs(self):
        return len(self.diffs) > 0


    def only_left(self):
        a = self.l[:]
        [a.remove(v) for v in self.equal]
        return a

    def only_right(self):
        a = self.r[:]
        [a.remove(v) for v in self.equal]
        return a


    def __str__(self, verbose:bool=False):
        iD = 0
        sb = []
        if verbose:
            sb.append(f"left: {self.l}\n")
            sb.append(f"right: {self.r}\n")
            sb.append(f"diffs: ")
        for tag, i, j, k, l in self.diffs:
            s = f"({iD})"
            if iD > 0: sb.append(' | ')
            if tag in ('delete', 'replace'): s = f'{s} l:{self.l[i:j]}'
            if tag in ('insert', 'replace'): s = f'{s} r:{self.r[k:l]}'
            sb.append(s)
            iD = iD + 1

        if verbose:
            sb.append(f"\nequal: {self.equal}")
        return ''.join(sb)

    def __repr__(self) -> str:
        return "<ListDiffer> {}".format(self.__str__())

用法:

left = ['a','b','c']
right = ['aa','b','c','d']
# right = ('aa','b','c','d')
ld = ListDiffer(left, right, strict=True)
print(f'ld.has_diffs(): {ld.has_diffs()}')
print(f'ld: {ld}')
print(f'ld.only_left(): {ld.only_left()}')
print(f'ld.only_right(): {ld.only_right()}')

输出:

ld.has_diffs(): True
ld: (0) l:['a'] r:['aa'] | (1) r:['d']
ld.only_left(): ['a']
ld.only_right(): ['aa', 'd']

我不能说性能,但你可以使用ld.only_left()来获得你正在寻找的结果。

试试这个:

temp3 = set(temp1) - set(temp2)

这里有一个最简单情况的反答案。

这比上面那个做双向差分的要短,因为它只做了问题要求的事情:生成第一个列表中的东西的列表,而不是第二个列表中的东西。

from collections import Counter

lst1 = ['One', 'Two', 'Three', 'Four']
lst2 = ['One', 'Two']

c1 = Counter(lst1)
c2 = Counter(lst2)
diff = list((c1 - c2).elements())

或者,根据你的可读性偏好,它可以是一个不错的单行代码:

diff = list((Counter(lst1) - Counter(lst2)).elements())

输出:

['Three', 'Four']

请注意,如果您只是对其进行迭代,则可以删除list(…)调用。

因为这个解决方案使用计数器,所以相对于许多基于集合的答案,它可以正确地处理数量。例如,在这个输入中:

lst1 = ['One', 'Two', 'Two', 'Two', 'Three', 'Three', 'Four']
lst2 = ['One', 'Two']

输出结果为:

['Two', 'Two', 'Three', 'Three', 'Four']