比较序列的优雅方式

时间:2009-05-22 23:32:57

标签: python

python是否提供了一种检查不同类型序列“相等”的优雅方法?以下工作,但对于python代码来说它们看起来相当丑陋和冗长:

def comp1(a, b):
    if len(a) != len(b):
        return False
    for i, v in enumerate(a):
        if v != b[i]:
            return False
    return True

以下内容有点短,但由于创建了第三个序列,效率也会降低:

def comp2(a, b):
    for l, r in map(None, a, b):
        if l != r:
            return False
    return True

将其中一个例子写入列表理解并不是我正在寻找的。

编辑:理想情况下,我正在寻找一种在比较期间不会创建另一个序列的解决方案。

8 个答案:

答案 0 :(得分:17)

将两个序列转换为列表,并使用内置列表比较。它应该足够了,除非你的序列非常大。

list(a) == list(b)

编辑:

schickb完成的测试显示使用元组稍快一些:

tuple(a) == tuple(b)

答案 1 :(得分:12)

您可以使用以下内容确定任何两个迭代(字符串,元组,列表,甚至自定义序列)的相等性,而无需创建和存储重复列表:

all(x == y for x, y in itertools.izip_longest(a, b))

请注意,如果两个迭代的长度不同,则较短的一个将填充None s。换句话说,它会将[1, 2, None]视为等于(1, 2)

编辑:正如卡米尔在评论中指出的那样,izip_longest仅适用于Python 2.6。但是,the docs for the function还提供了一个替代实现,它应该一直工作到2.3。

编辑2:在几台不同的计算机上进行测试后,在某些情况下,这似乎只比list(a) == list(b)更快,我无法隔离。大多数情况下,它需要大约七倍的时间。但是,我还发现tuple(a) == tuple(b)始终至少是list版本的两倍。

答案 2 :(得分:5)

除了创建临时列表/元组所使用的额外内存之外,当序列中早期出现不等式时,这些答案将会丢失为大序列的短路生成器解决方案

from itertools import starmap, izip
from operator import eq
all(starmap(eq, izip(x, y)))

或更简洁

from itertools import imap
from operator import eq
all(imap(eq, x, y))

来自ipython的一些基准

x=range(1000)
y=range(1000); y[10]=0

timeit tuple(x) == tuple(y)
100000 loops, best of 3: 16.9 us per loop

timeit all(imap(eq, x, y))
100000 loops, best of 3: 2.86 us per loop

答案 3 :(得分:2)

看起来像元组(a)==元组(b)是最好的整体选择。或者也许与前面的len检查进行元组比较,如果它们通常是不同的长度。这确实创建了额外的列表,但希望不是一个问题,除了真正庞大的列表。以下是我对各种替代方案的比较:

import timeit

tests = (
'''
a=b=[5]*100
''',

'''
a=[5]*100
b=[5]*3
''',

'''
a=b=(5,)*100
''',

'''
a=b="This on is a string" * 5
''',

'''
import array
a=b=array.array('B', "This on is a string" * 5)
'''
)

common = '''import itertools
def comp1(a, b):
    if len(a) != len(b):
        return False
    for i, v in enumerate(a):
        if v != b[i]:
            return False
    return True'''

for i, setup in enumerate(tests):
    t1 = timeit.Timer("comp1(a, b)", setup + common)
    t2 = timeit.Timer("all(x == y for x, y in itertools.izip_longest(a, b))", setup + common)
    t3 = timeit.Timer("all([x == y for x, y in itertools.izip_longest(a, b)])", setup + common)
    t4 = timeit.Timer("list(a) == list(b)", setup + common)
    t5 = timeit.Timer("tuple(a) == tuple(b)", setup + common)

    print '==test %d==' % i
    print '   comp1: %g' % t1.timeit()
    print ' all gen: %g' % t2.timeit()
    print 'all list: %g' % t3.timeit()
    print '    list: %g' % t4.timeit()
    print '   tuple: %g\n' % t5.timeit()

结果如下:

==test 0==
   comp1: 27.8089
 all gen: 31.1406
all list: 29.4887
    list: 3.58438
   tuple: 3.25859

==test 1==
   comp1: 0.833313
 all gen: 3.8026
all list: 33.5288
    list: 1.90453
   tuple: 1.74985

==test 2==
   comp1: 30.606
 all gen: 31.4755
all list: 29.5637
    list: 3.56635
   tuple: 1.60032

==test 3==
   comp1: 33.3725
 all gen: 35.3699
all list: 34.2619
    list: 10.2443
   tuple: 10.1124

==test 4==
   comp1: 31.7014
 all gen: 32.0051
all list: 31.0664
    list: 8.35031
   tuple: 8.16301

修改:添加了一些测试。这是在带有2GB内存的AMD 939 3800+上运行的。 Linux 32bit,Python 2.6.2

答案 4 :(得分:1)

由于您在引号中加上“相等”一词,我假设您想知道列表是如何相同以及它们是如何不同的。查看具有SequenceMatcher类的 difflib

    sm = difflib.SequenceMatcher(None, a, b)
    for opcode in sm.get_opcodes():
        print "    (%s %d:%d %d:%d)" % opcode

您将获得一系列差异的描述。将其转换为 diff 类似的输出相当简单。

答案 5 :(得分:0)

它可能效率不高,但看起来很时髦:

def cmpLists(a, b):
    return len(a) == len(b) and (False not in [a[i] == b[i] for i in range(0,len(a)])

我不知道Ben mentioned的“全部”功能,但也许您可以使用它而不是“假而不是”

答案 6 :(得分:0)

这个“功能”代码应该足够快速和通用,适用于所有目的。

# python 2.6 ≤ x < 3.0
import operator, itertools as it

def seq_cmp(seqa, seqb):
    return all(it.starmap(operator.eq, it.izip_longest(seqa, seqb)))

如果在Python 2.5上,请使用there中的izip_longest定义。

答案 7 :(得分:0)

我认为当两个序列都是list类型时,特殊情况是个好主意。比较两个列表比将两个列表都转换为元组更快(并且内存效率更高)。

如果ab不是列表,则它们都会转换为tuple。如果一个或两个都已经是元组,则没有开销,因为在这种情况下tuple()只返回对原始对象的引用。

def comp(a, b):
    if len(a) != len(b):
        return False
    if type(a) == type(b) == list:
        return a == b
    a = tuple(a)
    b = tuple(b)
    return a == b